import { router, publicProcedure, protectedProcedure } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import * as allGamesService from "../services/allGamesService.ts";
import { getDb } from "../db.ts";
import { allGames, gameSessions } from "../../drizzle/schema.ts";
import { eq, like, and, desc, asc, sql } from "drizzle-orm";

// Admin-only middleware
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 allGamesRouter = router({
  /**
   * Get all games with optional filters
   */
  getAll: publicProcedure
    .input(
      z.object({
        provider: z.string().optional(),
        category: z.string().optional(),
        volatility: z.string().optional(),
        featured: z.boolean().optional(),
        search: z.string().optional(),
        limit: z.number().min(1).max(500).default(500),
        offset: z.number().min(0).default(0),
        sortBy: z.enum(["playCount", "gameName", "rtp", "provider"]).default("playCount"),
        sortDir: z.enum(["asc", "desc"]).default("desc"),
      }).optional()
    )
    .query(async ({ input }) => {
      return allGamesService.getAllGames(input);
    }),

  /**
   * Get game by ID
   */
  getById: publicProcedure
    .input(z.object({ gameId: z.string() }))
    .query(async ({ input }) => {
      return allGamesService.getGameById(input.gameId);
    }),

  /**
   * Get featured games
   */
  getFeatured: publicProcedure.query(async () => {
    return allGamesService.getFeaturedGames();
  }),

  /**
   * Get new arrivals (most recently added games)
   */
  getNewArrivals: publicProcedure
    .input(z.object({ limit: z.number().min(1).max(50).default(20) }).optional())
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) return [];
      const games = await db
        .select()
        .from(allGames)
        .where(eq(allGames.isActive, 1))
        .orderBy(desc(allGames.createdAt))
        .limit(input?.limit ?? 20);
      return games;
    }),

  /**
   * Search games
   */
  search: publicProcedure
    .input(z.object({ query: z.string() }))
    .query(async ({ input }) => {
      return allGamesService.searchGames(input.query);
    }),

  /**
   * Get games by provider
   */
  getByProvider: publicProcedure
    .input(z.object({ provider: z.string() }))
    .query(async ({ input }) => {
      return allGamesService.getGamesByProvider(input.provider);
    }),

  /**
   * Get user's game library
   */
  getUserLibrary: protectedProcedure.query(async ({ ctx }) => {
    return allGamesService.getUserGameLibrary(ctx.user.id);
  }),

  /**
   * Add game to user's library
   */
  addToLibrary: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const success = await allGamesService.addToGameLibrary(ctx.user.id, input.gameId);
      return { success };
    }),

  /**
   * Get game recommendations for user
   */
  getRecommendations: protectedProcedure
    .input(z.object({ limit: z.number().default(10) }).optional())
    .query(async ({ ctx, input }) => {
      return allGamesService.getGameRecommendations(ctx.user.id, input?.limit);
    }),

  /**
   * Update game stats after play
   */
  updateStats: protectedProcedure
    .input(
      z.object({
        gameId: z.string(),
        betAmount: z.number(),
        winAmount: z.number(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const success = await allGamesService.updateGameStats(
        input.gameId,
        ctx.user.id,
        input.betAmount,
        input.winAmount
      );
      return { success };
    }),

  /**
   * Get game statistics
   */
  getStats: publicProcedure
    .input(
      z.object({
        gameId: z.string(),
        days: z.number().default(7),
      })
    )
    .query(async ({ input }) => {
      return allGamesService.getGameStatistics(input.gameId, input.days);
    }),

  /**
   * Get all providers
   */
  getProviders: publicProcedure.query(async () => {
    const games = await allGamesService.getAllGames();
    const providers = new Set(games.map((g) => g.provider).filter(Boolean));
    return Array.from(providers).sort();
  }),

  /**
   * Get statistics summary
   */
  getStatsSummary: publicProcedure.query(async () => {
    const games = await allGamesService.getAllGames();
    const providers = new Set(games.map((g) => g.provider));
    const categories = new Set(games.map((g) => g.category));
    const volatilities = new Set(games.map((g) => g.volatility));
    const activeCount = games.filter((g) => g.isActive).length;
    const featuredCount = games.filter((g) => g.isFeatured === 1).length;

    return {
      totalGames: games.length,
      activeGames: activeCount,
      featuredGames: featuredCount,
      providers: Array.from(providers),
      categories: Array.from(categories),
      volatilities: Array.from(volatilities),
      avgRtp: games.length > 0
        ? (games.reduce((sum, g) => sum + parseFloat(g.rtp?.toString() || "0"), 0) / games.length).toFixed(2)
        : "0",
    };
  }),

  // ─── PG Soft Game Launch ─────────────────────────────────────────────────────

  /**
   * Launch a PG Soft game — generates a session token and returns the game URL
   */
  launchPgSoftGame: protectedProcedure
    .input(z.object({
      gameId: z.string(),   // e.g. "pgsoft-fortune-tiger"
      gameCode: z.string(), // e.g. "fortune-tiger"
    }))
    .mutation(async ({ ctx, input }) => {
      const PGSOFT_API = process.env.PGSOFT_API_URL || "http://localhost:3001";
      const AGENT_TOKEN = process.env.PGSOFT_AGENT_TOKEN || "coinkrazy_agent";
      const AGENT_SECRET = process.env.PGSOFT_AGENT_SECRET || "coinkrazy_secret";

      // Use the user's CoinKrazy ID as the PG Soft user_code
      const userCode = `ck_${ctx.user.id}`;
      const userBalance = 1000; // Social casino — virtual balance

      try {
        const response = await fetch(`${PGSOFT_API}/api/v1/game_launch`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            agentToken: AGENT_TOKEN,
            secretKey: AGENT_SECRET,
            user_code: userCode,
            user_balance: userBalance,
            game_code: input.gameCode,
            game_type: "slot",
            provider_code: "PGSOFT",
          }),
        });

        const data = await response.json() as any;

        if (data.status !== 1) {
          throw new TRPCError({
            code: "INTERNAL_SERVER_ERROR",
            message: data.message || "Failed to launch game",
          });
        }

        return {
          success: true,
          launchUrl: data.launch_url as string,
          userCode: data.user_code as string,
        };
      } catch (error: any) {
        if (error instanceof TRPCError) throw error;
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: "PG Soft server unavailable",
        });
      }
    }),

  // ─── Slot Engine Game Launch ─────────────────────────────────────────────────
  /**
   * Launch a slot game using the built-in HTML5 slot engine
   * Used for Yggdrasil, NextGen, AGS, PlayGD games
   */
  launchSlotGame: protectedProcedure
    .input(z.object({
      gameId: z.string(),
      gameName: z.string(),
      provider: z.string(),
      rtp: z.string().optional(),
      volatility: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Build the slot engine URL with game parameters
      const params = new URLSearchParams({
        name: input.gameName,
        provider: input.provider,
        rtp: input.rtp || '96',
        volatility: input.volatility || 'medium',
        gameId: input.gameId,
        userId: String(ctx.user.id),
      });
      const launchUrl = `/slot-engine.html?${params.toString()}`;
      return {
        success: true,
        launchUrl,
      };
    }),

  // ─── Recently Played ──────────────────────────────────────────────────────────

  /**
   * Get the last 6 unique games the current user played
   */
  getRecentlyPlayed: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) return [];
    try {
      // Get last 12 sessions, join with all_games to get game details
      const sessions = await db
        .select({
          sessionId: gameSessions.id,
          gameRowId: gameSessions.gameId,
          playedAt: gameSessions.createdAt,
        })
        .from(gameSessions)
        .where(eq(gameSessions.userId, ctx.user.id))
        .orderBy(desc(gameSessions.createdAt))
        .limit(24);

      if (!sessions.length) return [];

      // Deduplicate by gameRowId, keep most recent
      const seen = new Set<number>();
      const uniqueGameIds: number[] = [];
      for (const s of sessions) {
        if (!seen.has(s.gameRowId)) {
          seen.add(s.gameRowId);
          uniqueGameIds.push(s.gameRowId);
          if (uniqueGameIds.length >= 6) break;
        }
      }

      // Fetch game details
      const games = await db
        .select()
        .from(allGames)
        .where(sql`${allGames.id} IN (${sql.join(uniqueGameIds.map(id => sql`${id}`), sql`, `)})`);

      // Return in play order
      return uniqueGameIds
        .map(id => games.find((g: any) => g.id === id))
        .filter(Boolean);
    } catch {
      return [];
    }
  }),

  // ─── Admin Procedures ────────────────────────────────────────────────────────

  /**
   * Admin: Toggle game active status
   */
  adminToggleActive: adminProcedure
    .input(z.object({ gameId: z.string(), isActive: z.boolean() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
      await db.update(allGames)
        .set({ isActive: input.isActive ? 1 : 0 })
        .where(eq(allGames.gameId, input.gameId));
      return { success: true };
    }),

  /**
   * Admin: Toggle game featured status
   */
  adminToggleFeatured: adminProcedure
    .input(z.object({ gameId: z.string(), isFeatured: z.boolean() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
      await db.update(allGames)
        .set({ isFeatured: input.isFeatured ? 1 : 0 })
        .where(eq(allGames.gameId, input.gameId));
      return { success: true };
    }),

  /**
   * Admin: Update game details
   */
  adminUpdateGame: adminProcedure
    .input(z.object({
      gameId: z.string(),
      gameName: z.string().optional(),
      rtp: z.string().optional(),
      volatility: z.enum(["low", "medium", "high", "very-high"]).optional(),
      category: z.string().optional(),
      provider: z.string().optional(),
      iconUrl: z.string().optional(),
    }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
      const updates: Record<string, any> = {};
      if (input.gameName) updates.gameName = input.gameName;
      if (input.rtp) updates.rtp = input.rtp;
      if (input.volatility) updates.volatility = input.volatility;
      if (input.category) updates.category = input.category;
      if (input.provider) updates.provider = input.provider;
      if (input.iconUrl !== undefined) updates.iconUrl = input.iconUrl;
      await db.update(allGames).set(updates).where(eq(allGames.gameId, input.gameId));
      return { success: true };
    }),

  /**
   * Admin: Bulk toggle active status for a provider
   */
  adminBulkToggleProvider: adminProcedure
    .input(z.object({ provider: z.string(), isActive: z.boolean() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
      const result = await db.update(allGames)
        .set({ isActive: input.isActive ? 1 : 0 })
        .where(eq(allGames.provider, input.provider));
      return { success: true };
    }),

  /**
   * Admin: Get paginated games list with full details
   */
  adminGetGames: adminProcedure
    .input(z.object({
      limit: z.number().min(1).max(100).default(50),
      offset: z.number().min(0).default(0),
      provider: z.string().optional(),
      category: z.string().optional(),
      search: z.string().optional(),
      activeOnly: z.boolean().optional(),
      featuredOnly: z.boolean().optional(),
    }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });

      const conditions: any[] = [];
      if (input.provider) conditions.push(eq(allGames.provider, input.provider));
      if (input.category) conditions.push(eq(allGames.category, input.category as any));
      if (input.search) conditions.push(like(allGames.gameName, `%${input.search}%`));
      if (input.activeOnly) conditions.push(eq(allGames.isActive, 1));
      if (input.featuredOnly) conditions.push(eq(allGames.isFeatured, 1));

      const query = db.select().from(allGames);
      if (conditions.length > 0) query.where(and(...conditions));

      const games = await query
        .orderBy(desc(allGames.playCount))
        .limit(input.limit)
        .offset(input.offset);

      // Get total count
      const countQuery = db.select({ count: sql<number>`count(*)` }).from(allGames);
      if (conditions.length > 0) countQuery.where(and(...conditions));
      const [{ count }] = await countQuery;

      return { games, total: Number(count) };
    }),
});
