import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { protectedProcedure, router } from "../_core/trpc.ts";
import { getDb, writeAuditLog } from "../db.ts";
import { eq, desc, like, and } from "drizzle-orm";
import { casinoGames, users, kycDocuments, auditLogs } from "../../drizzle/schema.ts";

const adminProcedure = protectedProcedure.use(({ ctx, next }) => {
  if (ctx.user.role !== "admin") {
    throw new TRPCError({ code: "FORBIDDEN", message: "Admin access required" });
  }
  return next({ ctx });
});

export const adminOperationsRouter = router({
  // ============ GAMES MANAGEMENT ============
  
  listGames: adminProcedure
    .input(z.object({
      page: z.number().default(1),
      limit: z.number().default(20),
      search: z.string().optional(),
      category: z.string().optional(),
      status: z.enum(["all", "active", "inactive"]).default("all"),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      let query = db.select().from(casinoGames);
      
      if (input.search) {
        query = query.where(like(casinoGames.title, `%${input.search}%`));
      }
      
      if (input.category && input.category !== "all") {
        query = query.where(eq(casinoGames.category, input.category));
      }

      const offset = (input.page - 1) * input.limit;
      const games = await query.limit(input.limit).offset(offset);
      
      // Get total count
      const countResult = await db.select().from(casinoGames);
      const total = countResult.length;
      const activeCount = countResult.filter(g => g.isActive).length;
      const totalPlayers = countResult.reduce((sum, g) => sum + (g.playCount || 0), 0);
      const totalRevenue = countResult.reduce((sum, g) => sum + (g.revenue || 0), 0);

      return {
        games,
        total,
        activeCount,
        totalPlayers,
        totalRevenue,
        page: input.page,
        pages: Math.ceil(total / input.limit),
      };
    }),

  toggleGameStatus: adminProcedure
    .input(z.object({
      gameId: z.number(),
      isActive: z.boolean(),
      reason: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      await db.update(casinoGames)
        .set({ isActive: input.isActive })
        .where(eq(casinoGames.id, input.gameId));

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: input.isActive ? "game_activated" : "game_deactivated",
        category: "games",
        details: { gameId: input.gameId, reason: input.reason },
      });

      return { success: true };
    }),

  bulkToggleGames: adminProcedure
    .input(z.object({
      gameIds: z.array(z.number()),
      isActive: z.boolean(),
      reason: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      for (const gameId of input.gameIds) {
        await db.update(casinoGames)
          .set({ isActive: input.isActive })
          .where(eq(casinoGames.id, gameId));
      }

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "bulk_games_toggled",
        category: "games",
        details: { gameIds: input.gameIds, isActive: input.isActive, reason: input.reason },
      });

      return { success: true, count: input.gameIds.length };
    }),

  // ============ USERS MANAGEMENT ============

  listUsers: adminProcedure
    .input(z.object({
      page: z.number().default(1),
      limit: z.number().default(20),
      search: z.string().optional(),
      role: z.string().optional(),
      status: z.enum(["all", "active", "suspended", "inactive"]).default("all"),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

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

      if (input.search) {
        query = query.where(
          or(
            like(users.username, `%${input.search}%`),
            like(users.email, `%${input.search}%`)
          )
        );
      }

      const offset = (input.page - 1) * input.limit;
      const userList = await query.limit(input.limit).offset(offset);

      return {
        users: userList,
        total: userList.length,
        page: input.page,
      };
    }),

  suspendUser: adminProcedure
    .input(z.object({
      userId: z.number(),
      reason: z.string().min(1),
      duration: z.number().optional(), // in days
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const suspendedUntil = input.duration
        ? new Date(Date.now() + input.duration * 86400000)
        : null;

      await db.update(users)
        .set({ isSuspended: true, suspendedUntil, suspendReason: input.reason })
        .where(eq(users.id, input.userId));

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        targetUserId: input.userId,
        action: "user_suspended",
        category: "users",
        details: { reason: input.reason, duration: input.duration },
      });

      return { success: true };
    }),

  promoteToVIP: adminProcedure
    .input(z.object({
      userIds: z.array(z.number()),
      vipTier: z.number().default(1),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      for (const userId of input.userIds) {
        await db.update(users)
          .set({ role: "vip", vipTier: input.vipTier })
          .where(eq(users.id, userId));
      }

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        action: "bulk_users_promoted_vip",
        category: "users",
        details: { userIds: input.userIds, vipTier: input.vipTier },
      });

      return { success: true, count: input.userIds.length };
    }),

  // ============ KYC MANAGEMENT ============

  listPendingKYC: adminProcedure
    .input(z.object({
      page: z.number().default(1),
      limit: z.number().default(20),
      riskLevel: z.enum(["all", "low", "medium", "high"]).default("all"),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const docs = await db.select().from(kycDocuments)
        .where(eq(kycDocuments.status, "pending"))
        .limit(input.limit)
        .offset((input.page - 1) * input.limit);

      return {
        applications: docs,
        total: docs.length,
        page: input.page,
      };
    }),

  approveKYC: adminProcedure
    .input(z.object({
      docId: z.number(),
      userId: z.number(),
      notes: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      await db.update(kycDocuments)
        .set({
          status: "approved",
          reviewedBy: ctx.user.id,
          reviewNote: input.notes,
          reviewedAt: new Date(),
        })
        .where(eq(kycDocuments.id, input.docId));

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        targetUserId: input.userId,
        action: "kyc_approved",
        category: "kyc",
        details: { docId: input.docId, notes: input.notes },
      });

      return { success: true };
    }),

  rejectKYC: adminProcedure
    .input(z.object({
      docId: z.number(),
      userId: z.number(),
      reason: z.string().min(1),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      await db.update(kycDocuments)
        .set({
          status: "rejected",
          reviewedBy: ctx.user.id,
          reviewNote: input.reason,
          reviewedAt: new Date(),
        })
        .where(eq(kycDocuments.id, input.docId));

      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "admin",
        targetUserId: input.userId,
        action: "kyc_rejected",
        category: "kyc",
        details: { docId: input.docId, reason: input.reason },
      });

      return { success: true };
    }),

  // ============ AUDIT LOGS ============

  listAuditLogs: adminProcedure
    .input(z.object({
      page: z.number().default(1),
      limit: z.number().default(50),
      action: z.string().optional(),
      adminId: z.number().optional(),
      dateRange: z.enum(["24h", "7d", "30d", "all"]).default("7d"),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const now = new Date();
      let since: Date;

      switch (input.dateRange) {
        case "24h":
          since = new Date(now.getTime() - 24 * 60 * 60 * 1000);
          break;
        case "7d":
          since = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
          break;
        case "30d":
          since = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
          break;
        default:
          since = new Date(0);
      }

      let query = db.select().from(auditLogs)
        .where(and(
          input.adminId ? eq(auditLogs.actorId, input.adminId) : undefined,
          input.action ? eq(auditLogs.action, input.action) : undefined
        ))
        .orderBy(desc(auditLogs.createdAt));

      const logs = await query.limit(input.limit).offset((input.page - 1) * input.limit);

      return {
        logs,
        total: logs.length,
        page: input.page,
      };
    }),

  exportAuditLogs: adminProcedure
    .input(z.object({
      dateRange: z.enum(["24h", "7d", "30d", "all"]).default("7d"),
      format: z.enum(["csv", "json"]).default("csv"),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const logs = await db.select().from(auditLogs)
        .orderBy(desc(auditLogs.createdAt));

      if (input.format === "json") {
        return { data: JSON.stringify(logs, null, 2), format: "json" };
      }

      // CSV format
      const headers = ["Timestamp", "Admin", "Action", "Category", "Target User", "Details"];
      const rows = logs.map(log => [
        log.createdAt.toISOString(),
        log.actorId.toString(),
        log.action,
        log.category,
        log.targetUserId?.toString() || "N/A",
        log.details ? JSON.stringify(log.details) : "",
      ]);

      const csv = [headers, ...rows].map(row => row.map(cell => `"${cell}"`).join(",")).join("\n");
      return { data: csv, format: "csv" };
    }),
});

// Helper function for OR conditions
function or(...conditions: any[]) {
  return conditions.filter(Boolean)[0];
}
