import { z } from "zod";
import { router, protectedProcedure } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { getDb } from "../db.ts";
import { eq, and } from "drizzle-orm";
import { fraudAlerts, users } from "../../drizzle/schema.ts";
import { getFraudAlerts, updateFraudAlertStatus } from "../services/fraudDetection.ts";

/**
 * Admin-only router for fraud alert management
 */
export const adminFraudAlertsRouter = router({
  /**
   * Get all fraud alerts with filtering
   */
  listAlerts: protectedProcedure
    .input(
      z.object({
        status: z.enum(["open", "investigating", "resolved", "dismissed"]).optional(),
        severity: z.enum(["low", "medium", "high", "critical"]).optional(),
        limit: z.number().min(1).max(100).default(50),
        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");

      const conditions = [];
      if (input.status) conditions.push(eq(fraudAlerts.status, input.status));
      if (input.severity) conditions.push(eq(fraudAlerts.severity, input.severity));

      let query = db.select().from(fraudAlerts);

      if (conditions.length > 0) {
        query = query.where(and(...conditions)) as any;
      }

      const alerts = await query
        .orderBy(fraudAlerts.createdAt)
        .limit(input.limit)
        .offset(input.offset);

      // Fetch user details for each alert
      const alertsWithUsers = await Promise.all(
        alerts.map(async (alert) => {
          const user = await db
            .select()
            .from(users)
            .where(eq(users.id, alert.userId))
            .limit(1);

          return {
            id: alert.id,
            userId: alert.userId,
            userName: user[0]?.name || "Unknown",
            userEmail: user[0]?.email || "Unknown",
            alertType: alert.alertType,
            severity: alert.severity,
            description: alert.description,
            details: alert.details ? JSON.parse(alert.details) : {},
            status: alert.status,
            createdAt: alert.createdAt,
            resolvedAt: alert.resolvedAt,
            resolvedBy: alert.resolvedBy,
          };
        })
      );

      return alertsWithUsers;
    }),

  /**
   * Get fraud alerts for a specific user
   */
  getUserAlerts: 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");

      const alerts = await db
        .select()
        .from(fraudAlerts)
        .where(eq(fraudAlerts.userId, input.userId))
        .orderBy(fraudAlerts.createdAt)
        .limit(input.limit)
        .offset(input.offset);

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

  /**
   * Get fraud statistics
   */
  getStats: protectedProcedure.query(async ({ ctx }) => {
    // 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");

    const allAlerts = await db.select().from(fraudAlerts);

    const stats = {
      total: allAlerts.length,
      byStatus: {
        open: allAlerts.filter((a) => a.status === "open").length,
        investigating: allAlerts.filter((a) => a.status === "investigating").length,
        resolved: allAlerts.filter((a) => a.status === "resolved").length,
        dismissed: allAlerts.filter((a) => a.status === "dismissed").length,
      },
      bySeverity: {
        low: allAlerts.filter((a) => a.severity === "low").length,
        medium: allAlerts.filter((a) => a.severity === "medium").length,
        high: allAlerts.filter((a) => a.severity === "high").length,
        critical: allAlerts.filter((a) => a.severity === "critical").length,
      },
      byType: {
        abnormal_rtp: allAlerts.filter((a) => a.alertType === "abnormal_rtp").length,
        rapid_betting: allAlerts.filter((a) => a.alertType === "rapid_betting").length,
        multi_account: allAlerts.filter((a) => a.alertType === "multi_account").length,
        bonus_abuse: allAlerts.filter((a) => a.alertType === "bonus_abuse").length,
        unusual_withdrawal: allAlerts.filter((a) => a.alertType === "unusual_withdrawal").length,
        velocity: allAlerts.filter((a) => a.alertType === "velocity").length,
        pattern: allAlerts.filter((a) => a.alertType === "pattern").length,
      },
      criticalAlerts: allAlerts.filter((a) => a.severity === "critical" && a.status === "open").length,
    };

    return stats;
  }),

  /**
   * Update alert status
   */
  updateStatus: protectedProcedure
    .input(
      z.object({
        alertId: z.number(),
        status: z.enum(["open", "investigating", "resolved", "dismissed"]),
        notes: z.string().optional(),
      })
    )
    .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");

      await updateFraudAlertStatus(input.alertId, input.status, ctx.user.id);

      // Log the action
      console.log(
        `[Fraud Alert] Admin ${ctx.user.id} updated alert ${input.alertId} to ${input.status}${
          input.notes ? ` (${input.notes})` : ""
        }`
      );

      return { success: true };
    }),

  /**
   * Get high-risk users (multiple critical alerts)
   */
  getHighRiskUsers: protectedProcedure
    .input(
      z.object({
        minCriticalAlerts: z.number().min(1).default(3),
        limit: z.number().min(1).max(100).default(20),
      })
    )
    .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");

      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;
        }
      }

      // Filter users with minimum critical alerts
      const highRiskUserIds = Object.entries(userCriticalCounts)
        .filter(([_, count]) => count >= input.minCriticalAlerts)
        .sort((a, b) => b[1] - a[1])
        .slice(0, input.limit)
        .map(([userId]) => parseInt(userId));

      // Fetch user details
      const highRiskUsers = await Promise.all(
        highRiskUserIds.map(async (userId) => {
          const user = await db
            .select()
            .from(users)
            .where(eq(users.id, userId))
            .limit(1);

          const userAlerts = allAlerts.filter((a) => a.userId === userId);

          return {
            userId,
            name: user[0]?.name || "Unknown",
            email: user[0]?.email || "Unknown",
            criticalAlertCount: userCriticalCounts[userId],
            totalAlertCount: userAlerts.length,
            lastAlertAt: userAlerts.length > 0 ? userAlerts[userAlerts.length - 1].createdAt : null,
            alertTypes: [...new Set(userAlerts.map((a) => a.alertType))],
          };
        })
      );

      return highRiskUsers;
    }),
});
