import { router, protectedProcedure, adminProcedure } from "../_core/trpc.ts";
import { z } from "zod";
import { getDb } from "../db.ts";
import { casinoGames, gameSessions, transactions } from "../../drizzle/schema.ts";
import { eq, and, desc, like, sql } from "drizzle-orm";
import { TRPCError } from "@trpc/server";

// Game configuration schema
const gameConfigSchema = z.object({
  rtp: z.number().min(70).max(99).optional(),
  volatility: z.enum(["low", "medium", "high"]).optional(),
  maxWin: z.number().positive().optional(),
  bonusFrequency: z.number().min(0).max(100).optional(),
  freeSpinsMultiplier: z.number().positive().optional(),
  scatterSymbolCount: z.number().min(1).max(5).optional(),
  wildSymbolCount: z.number().min(0).max(5).optional(),
});

const createGameSchema = z.object({
  gameName: z.string().min(3).max(255),
  description: z.string().optional(),
  provider: z.string().min(1).max(100),
  category: z.string().min(1).max(50),
  rtp: z.number().min(70).max(99).default(96),
  volatility: z.enum(["low", "medium", "high"]).default("medium"),
  maxWin: z.number().positive().default(10000),
  thumbnailUrl: z.string().url().optional(),
  isActive: z.boolean().default(true),
  config: gameConfigSchema.optional(),
});

export const gameManagerRouter = router({
  // Get all games with filtering and pagination
  listGames: adminProcedure
    .input(
      z.object({
        page: z.number().min(1).default(1),
        limit: z.number().min(1).max(100).default(20),
        search: z.string().optional(),
        category: z.string().optional(),
        isActive: z.boolean().optional(),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      const offset = (input.page - 1) * input.limit;

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

      if (input.search) {
        query = query.where(like(casinoGames.gameName, `%${input.search}%`));
      }

      if (input.category) {
        query = query.where(eq(casinoGames.category, input.category));
      }

      if (input.isActive !== undefined) {
        query = query.where(eq(casinoGames.isActive, input.isActive ? 1 : 0));
      }

      const total = await db
        .select({ count: sql<number>`count(*)` })
        .from(casinoGames)
        .then((res) => res[0]?.count || 0);

      const games = await query
        .orderBy(desc(casinoGames.updatedAt))
        .limit(input.limit)
        .offset(offset);

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

  // Get single game details
  getGame: adminProcedure
    .input(z.object({ gameId: z.number() }))
    .query(async ({ input }) => {
      const db = await getDb();
      const game = await db
        .select()
        .from(casinoGames)
        .where(eq(casinoGames.id, input.gameId))
        .then((res) => res[0]);

      if (!game) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "Game not found",
        });
      }

      return game;
    }),

  // Update game configuration
  updateGameConfig: adminProcedure
    .input(
      z.object({
        gameId: z.number(),
        config: gameConfigSchema,
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();

      const game = await db
        .select()
        .from(casinoGames)
        .where(eq(casinoGames.id, input.gameId))
        .then((res) => res[0]);

      if (!game) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "Game not found",
        });
      }

      // Update game with new config
      const updatedGame = await db
        .update(casinoGames)
        .set({
          rtp: input.config.rtp || game.rtp,
          volatility: input.config.volatility || game.volatility,
          maxWin: input.config.maxWin || game.maxWin,
          updatedAt: new Date().toISOString(),
        })
        .where(eq(casinoGames.id, input.gameId));

      // Log audit trail
      await db.insert(transactions).values({
        userId: ctx.user.id,
        type: "admin_game_config",
        currency: "GC",
        amount: 0,
        balanceBefore: 0,
        balanceAfter: 0,
        referenceId: input.gameId.toString(),
        referenceType: "game",
        description: `Updated game configuration for ${game.gameName}`,
        metadata: JSON.stringify(input.config),
      });

      return { success: true, gameId: input.gameId };
    }),

  // Toggle game active status
  toggleGameStatus: adminProcedure
    .input(
      z.object({
        gameId: z.number(),
        isActive: z.boolean(),
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();

      const game = await db
        .select()
        .from(casinoGames)
        .where(eq(casinoGames.id, input.gameId))
        .then((res) => res[0]);

      if (!game) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "Game not found",
        });
      }

      await db
        .update(casinoGames)
        .set({
          isActive: input.isActive ? 1 : 0,
          updatedAt: new Date().toISOString(),
        })
        .where(eq(casinoGames.id, input.gameId));

      // Log audit trail
      await db.insert(transactions).values({
        userId: ctx.user.id,
        type: "admin_game_status",
        currency: "GC",
        amount: 0,
        balanceBefore: 0,
        balanceAfter: 0,
        referenceId: input.gameId.toString(),
        referenceType: "game",
        description: `${input.isActive ? "Enabled" : "Disabled"} game: ${game.gameName}`,
      });

      return { success: true, isActive: input.isActive };
    }),

  // Get game analytics
  getGameAnalytics: adminProcedure
    .input(
      z.object({
        gameId: z.number(),
        days: z.number().min(1).max(365).default(7),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();

      const game = await db
        .select()
        .from(casinoGames)
        .where(eq(casinoGames.id, input.gameId))
        .then((res) => res[0]);

      if (!game) {
        throw new TRPCError({
          code: "NOT_FOUND",
          message: "Game not found",
        });
      }

      const daysAgo = new Date();
      daysAgo.setDate(daysAgo.getDate() - input.days);

      // Get game session analytics
      const analytics = await db
        .select({
          totalSpins: sql<number>`count(*)`,
          totalWagered: sql<number>`sum(cast(${gameSessions.betAmount} as decimal(15,2)))`,
          totalWon: sql<number>`sum(cast(${gameSessions.winAmount} as decimal(15,2)))`,
          uniquePlayers: sql<number>`count(distinct ${gameSessions.userId})`,
          avgBetSize: sql<number>`avg(cast(${gameSessions.betAmount} as decimal(10,2)))`,
        })
        .from(gameSessions)
        .where(
          and(
            eq(gameSessions.gameId, input.gameId),
            sql`${gameSessions.createdAt} >= ${daysAgo.toISOString()}`
          )
        );

      const stats = analytics[0] || {
        totalSpins: 0,
        totalWagered: 0,
        totalWon: 0,
        uniquePlayers: 0,
        avgBetSize: 0,
      };

      const revenue =
        (stats.totalWagered || 0) - (stats.totalWon || 0);
      const rtp =
        stats.totalWagered && stats.totalWagered > 0
          ? ((stats.totalWon || 0) / stats.totalWagered) * 100
          : 0;

      return {
        game,
        analytics: {
          ...stats,
          revenue,
          rtp: Math.round(rtp * 100) / 100,
          days: input.days,
        },
      };
    }),

  // Get top performing games
  getTopGames: adminProcedure
    .input(
      z.object({
        limit: z.number().min(1).max(50).default(10),
        days: z.number().min(1).max(365).default(7),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();

      const daysAgo = new Date();
      daysAgo.setDate(daysAgo.getDate() - input.days);

      const topGames = await db
        .select({
          gameId: gameSessions.gameId,
          gameName: casinoGames.gameName,
          totalSpins: sql<number>`count(*)`,
          totalWagered: sql<number>`sum(cast(${gameSessions.betAmount} as decimal(15,2)))`,
          totalWon: sql<number>`sum(cast(${gameSessions.winAmount} as decimal(15,2)))`,
          uniquePlayers: sql<number>`count(distinct ${gameSessions.userId})`,
        })
        .from(gameSessions)
        .innerJoin(casinoGames, eq(gameSessions.gameId, casinoGames.id))
        .where(sql`${gameSessions.createdAt} >= ${daysAgo.toISOString()}`)
        .groupBy(gameSessions.gameId, casinoGames.id, casinoGames.gameName)
        .orderBy(desc(sql`count(*)`))
        .limit(input.limit);

      return topGames.map((game) => ({
        ...game,
        revenue: (game.totalWagered || 0) - (game.totalWon || 0),
        rtp:
          game.totalWagered && game.totalWagered > 0
            ? Math.round(((game.totalWon || 0) / game.totalWagered) * 10000) /
              100
            : 0,
      }));
    }),

  // Get game categories
  getCategories: protectedProcedure.query(async () => {
    const db = await getDb();
    const categories = await db
      .selectDistinct({ category: casinoGames.category })
      .from(casinoGames)
      .where(eq(casinoGames.isActive, 1));

    return categories.map((c) => c.category).filter(Boolean);
  }),

  // Get game providers
  getProviders: protectedProcedure.query(async () => {
    const db = await getDb();
    const providers = await db
      .selectDistinct({ provider: casinoGames.provider })
      .from(casinoGames)
      .where(eq(casinoGames.isActive, 1));

    return providers.map((p) => p.provider).filter(Boolean);
  }),
});
