import { z } from "zod";
import { router, protectedProcedure } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { getDb } from "../db.ts";
import { eq } from "drizzle-orm";
import { users, fraudAlerts, wallets, transactions } from "../../drizzle/schema.ts";
import {
  executeAutomatedActions,
  overrideFraudAction,
  isAccountSuspended,
  areWithdrawalsHeld,
  getAccountRestrictions,
} from "../services/fraudActions.ts";
import { writeAuditLog } from "../db.ts";

/**
 * Admin router for managing fraud actions
 */
export const fraudActionsAdminRouter = router({
  /**
   * Manually execute fraud actions for a user
   */
  executeActions: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        alertId: z.number(),
        actions: z.array(
          z.enum(["suspend_account", "reverse_bonus", "hold_withdrawal", "flag_account", "limit_bets"])
        ),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      try {
        const results = await executeAutomatedActions(
          input.userId,
          input.alertId,
          "high",
          `Manual action by admin ${ctx.user.id}: ${input.reason}`
        );

        // Log admin action
        await writeAuditLog({
          actorId: ctx.user.id,
          actorRole: "admin",
          action: "manual_fraud_actions",
          category: "fraud",
          details: {
            userId: input.userId,
            alertId: input.alertId,
            actions: input.actions,
            reason: input.reason,
            results,
          },
        });

        return {
          success: true,
          message: `Executed ${results.length} action(s)`,
          results,
        };
      } catch (error) {
        console.error("[Fraud Actions Admin] Error executing actions:", error);
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "Failed to execute actions",
        });
      }
    }),

  /**
   * Lift account suspension
   */
  liftSuspension: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Lift suspension
      await db
        .update(users)
        .set({
          isActive: true,
          suspendedAt: null,
          suspensionReason: null,
          suspensionUntil: null,
        })
        .where(eq(users.id, input.userId));

      // Log action
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "lift_suspension",
        category: "fraud",
        details: {
          userId: input.userId,
          reason: input.reason,
        },
      });

      return { success: true, message: "Suspension lifted" };
    }),

  /**
   * Release withdrawal hold
   */
  releaseWithdrawalHold: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Release withdrawal hold
      await db
        .update(users)
        .set({
          withdrawalHoldUntil: null,
          withdrawalHoldReason: null,
        })
        .where(eq(users.id, input.userId));

      // Log action
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "release_withdrawal_hold",
        category: "fraud",
        details: {
          userId: input.userId,
          reason: input.reason,
        },
      });

      return { success: true, message: "Withdrawal hold released" };
    }),

  /**
   * Remove account flag
   */
  removeFlag: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Remove flag
      await db
        .update(users)
        .set({
          isFlagged: false,
          flaggedReason: null,
          flaggedAt: null,
        })
        .where(eq(users.id, input.userId));

      // Log action
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "remove_flag",
        category: "fraud",
        details: {
          userId: input.userId,
          reason: input.reason,
        },
      });

      return { success: true, message: "Account flag removed" };
    }),

  /**
   * Get account restrictions
   */
  getRestrictions: protectedProcedure
    .input(z.object({ userId: z.number() }))
    .query(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const restrictions = await getAccountRestrictions(input.userId);

      // Get user details
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const user = await db.select().from(users).where(eq(users.id, input.userId)).limit(1);

      return {
        userId: input.userId,
        userName: user[0]?.name || "Unknown",
        userEmail: user[0]?.email || "Unknown",
        ...restrictions,
      };
    }),

  /**
   * Reverse bonus manually
   */
  reverseBonus: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        amount: z.number().min(0.01).max(100000),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Get user wallet
      const wallets = await db.select().from(wallets).where(eq(wallets.userId, input.userId)).limit(1);

      if (!wallets[0]) {
        throw new TRPCError({ code: "NOT_FOUND", message: "Wallet not found" });
      }

      // Deduct from SC balance (bonus currency)
      const currentBalance = parseFloat(wallets[0].scBalance);
      const newBalance = Math.max(0, currentBalance - input.amount);

      await db
        .update(wallets)
        .set({
          scBalance: newBalance.toFixed(2),
        })
        .where(eq(wallets.userId, input.userId));

      // Log transaction
      await db.insert(transactions).values({
        userId: input.userId,
        type: "admin_debit",
        currency: "SC",
        amount: input.amount.toFixed(2),
        balanceBefore: currentBalance.toFixed(2),
        balanceAfter: newBalance.toFixed(2),
        description: `Manual bonus reversal by admin: ${input.reason}`,
        referenceId: `admin-${ctx.user.id}`,
        referenceType: "manual_reversal",
      });

      // Log audit
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "manual_bonus_reversal",
        category: "fraud",
        details: {
          userId: input.userId,
          amount: input.amount,
          reason: input.reason,
        },
      });

      return {
        success: true,
        message: `Reversed ${input.amount.toFixed(2)} SC from account`,
        newBalance: newBalance.toFixed(2),
      };
    }),

  /**
   * Get action history for user
   */
  getActionHistory: protectedProcedure
    .input(
      z.object({
        userId: z.number(),
        limit: z.number().min(1).max(100).default(20),
        offset: z.number().min(0).default(0),
      })
    )
    .query(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Get fraud alerts for user
      const alerts = await db
        .select()
        .from(fraudAlerts)
        .where((table) => `${table.userId} = ${input.userId}`)
        .orderBy((table) => `${table.createdAt} DESC`)
        .limit(input.limit)
        .offset(input.offset);

      return alerts.map((alert) => ({
        id: alert.id,
        alertType: alert.alertType,
        severity: alert.severity,
        description: alert.description,
        status: alert.status,
        createdAt: alert.createdAt,
        resolvedAt: alert.resolvedAt,
        resolvedBy: alert.resolvedBy,
        details: alert.details ? JSON.parse(alert.details) : {},
      }));
    }),

  /**
   * Bulk action: suspend multiple high-risk users
   */
  suspendHighRiskUsers: protectedProcedure
    .input(
      z.object({
        minCriticalAlerts: z.number().min(1).default(3),
        reason: z.string().min(10).max(500),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify admin role
      if (ctx.user.role !== "admin") {
        throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
      }

      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Get all critical alerts
      const allAlerts = await db.select().from(fraudAlerts);

      // Group by user and count critical alerts
      const userCriticalCounts: Record<number, number> = {};
      for (const alert of allAlerts) {
        if (alert.severity === "critical") {
          userCriticalCounts[alert.userId] = (userCriticalCounts[alert.userId] || 0) + 1;
        }
      }

      // Find users with minimum critical alerts
      const highRiskUserIds = Object.entries(userCriticalCounts)
        .filter(([_, count]) => count >= input.minCriticalAlerts)
        .map(([userId]) => parseInt(userId));

      // Suspend all high-risk users
      const suspensionUntil = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();

      for (const userId of highRiskUserIds) {
        await db
          .update(users)
          .set({
            isActive: false,
            suspendedAt: new Date().toISOString(),
            suspensionReason: `Bulk suspension: ${input.reason}`,
            suspensionUntil,
          })
          .where(eq(users.id, userId));
      }

      // Log action
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "bulk_suspend_high_risk",
        category: "fraud",
        details: {
          userCount: highRiskUserIds.length,
          minCriticalAlerts: input.minCriticalAlerts,
          reason: input.reason,
        },
      });

      return {
        success: true,
        message: `Suspended ${highRiskUserIds.length} high-risk users`,
        suspendedUserIds: highRiskUserIds,
      };
    }),
});
