import { z } from "zod";
import { protectedProcedure, publicProcedure, router } from "../_core/trpc.ts";
import { getDb } from "../db.ts";
import { leaderboards, userVipStatus, vipTiers, tournaments, tournamentParticipants, users } from "../../drizzle/schema.ts";
import { eq, desc, and, gte, lte, isNull } from "drizzle-orm";

export const engagementRouter = router({
  // ─── LEADERBOARDS ─────────────────────────────────────────────────────────

  getLeaderboard: publicProcedure
    .input(
      z.object({
        type: z.enum(["global", "weekly", "daily", "game_specific"]),
        gameId: z.number().optional(),
        limit: z.number().default(100),
        offset: z.number().default(0),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) return [];

      if (input.gameId) {
        return await db
          .select({
            rank: leaderboards.rank,
            userId: leaderboards.userId,
            username: users.username,
            avatarUrl: users.avatarUrl,
            totalWinnings: leaderboards.totalWinnings,
            winCount: leaderboards.winCount,
          })
          .from(leaderboards)
          .innerJoin(users, eq(leaderboards.userId, users.id))
          .where(and(eq(leaderboards.leaderboardType, input.type), eq(leaderboards.gameId, input.gameId)))
          .orderBy(desc(leaderboards.totalWinnings))
          .limit(input.limit)
          .offset(input.offset);
      }

      return await db
        .select({
          rank: leaderboards.rank,
          userId: leaderboards.userId,
          username: users.username,
          avatarUrl: users.avatarUrl,
          totalWinnings: leaderboards.totalWinnings,
          winCount: leaderboards.winCount,
        })
        .from(leaderboards)
        .innerJoin(users, eq(leaderboards.userId, users.id))
        .where(eq(leaderboards.leaderboardType, input.type))
        .orderBy(desc(leaderboards.totalWinnings))
        .limit(input.limit)
        .offset(input.offset);
    }),

  getUserLeaderboardPosition: protectedProcedure
    .input(
      z.object({
        type: z.enum(["global", "weekly", "daily", "game_specific"]),
        gameId: z.number().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) return null;

      const result = await db
        .select()
        .from(leaderboards)
        .where(
          and(
            eq(leaderboards.userId, ctx.user.id),
            eq(leaderboards.leaderboardType, input.type),
            input.gameId ? eq(leaderboards.gameId, input.gameId) : isNull(leaderboards.gameId)
          )
        )
        .limit(1);

      return result[0] || null;
    }),

  // ─── VIP SYSTEM ────────────────────────────────────────────────────────────

  getVipTiers: publicProcedure.query(async () => {
    const db = await getDb();
    if (!db) return [];

    return await db.select().from(vipTiers).orderBy(vipTiers.minLifetimeWinnings);
  }),

  getUserVipStatus: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) return null;

      const result = await db
        .select()
        .from(userVipStatus)
        .where(eq(userVipStatus.userId, ctx.user.id));

    if (!result[0]) {
      // Create initial VIP status
      await db.insert(userVipStatus).values([{
        userId: ctx.user.id,
        lifetimeWinnings: "0.00",
        totalBetsPlaced: "0.00",
      }]);

      return {
        userId: ctx.user.id,
        currentVipTierId: null,
        lifetimeWinnings: 0,
        totalBetsPlaced: 0,
      };
    }

    return result[0];
  }),

  claimWeeklyVipBonus: protectedProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("Database not available");

    const vipStatus = await db
      .select()
      .from(userVipStatus)
      .where(eq(userVipStatus.userId, ctx.user.id))
      .limit(1);

    if (!vipStatus[0]) throw new Error("VIP status not found");

    const lastClaimed = vipStatus[0].weeklyBonusClaimedAt;
    const now = new Date();
    const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

    if (lastClaimed && lastClaimed > sevenDaysAgo) {
      throw new Error("Weekly bonus already claimed");
    }

    const tier = vipStatus[0].currentVipTierId
      ? await db.select().from(vipTiers).where(eq(vipTiers.id, vipStatus[0].currentVipTierId)).limit(1)
      : null;

    const bonusAmount = tier?.[0]?.weeklyBonusGc || 0;

    if (bonusAmount > 0) {
      // Add bonus to wallet (simplified - in production, use proper transaction)
      await db
        .update(userVipStatus)
        .set({ weeklyBonusClaimedAt: now })
        .where(eq(userVipStatus.userId, ctx.user.id));

      return { success: true, bonusAmount };
    }

    return { success: false, bonusAmount: 0 };
  }),

  // ─── TOURNAMENTS ───────────────────────────────────────────────────────────

  listTournaments: publicProcedure
    .input(
      z.object({
        status: z.enum(["upcoming", "active", "completed", "cancelled"]).optional(),
        limit: z.number().default(20),
        offset: z.number().default(0),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) return [];

      if (input.status) {
        return await db
          .select()
          .from(tournaments)
          .where(eq(tournaments.status, input.status))
          .orderBy(desc(tournaments.startAt))
          .limit(input.limit)
          .offset(input.offset);
      }

      return await db
        .select()
        .from(tournaments)
        .orderBy(desc(tournaments.startAt))
        .limit(input.limit)
        .offset(input.offset);
    }),

  getTournamentDetails: publicProcedure
    .input(z.object({ tournamentId: z.number() }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) return null;

      const tournament = await db
        .select()
        .from(tournaments)
        .where(eq(tournaments.id, input.tournamentId))
        .limit(1);

      if (!tournament[0]) return null;

      const participants = await db
        .select({
          userId: tournamentParticipants.userId,
          username: users.username,
          avatarUrl: users.avatarUrl,
          score: tournamentParticipants.score,
          finalRank: tournamentParticipants.finalRank,
          prizeWon: tournamentParticipants.prizeWon,
        })
        .from(tournamentParticipants)
        .innerJoin(users, eq(tournamentParticipants.userId, users.id))
        .where(eq(tournamentParticipants.tournamentId, input.tournamentId))
        .orderBy(desc(tournamentParticipants.score));

      return { tournament: tournament[0], participants };
    }),

  joinTournament: protectedProcedure
    .input(z.object({ tournamentId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database not available");

      const tournament = await db
        .select()
        .from(tournaments)
        .where(eq(tournaments.id, input.tournamentId))
        .limit(1);

      if (!tournament[0]) throw new Error("Tournament not found");

      if (tournament[0].status !== "upcoming" && tournament[0].status !== "active") {
        throw new Error("Tournament is not accepting new participants");
      }

      if (tournament[0].maxParticipants && tournament[0].currentParticipants >= tournament[0].maxParticipants) {
        throw new Error("Tournament is full");
      }

      // Check if already joined
      const existing = await db
        .select()
        .from(tournamentParticipants)
        .where(
          and(
            eq(tournamentParticipants.tournamentId, input.tournamentId),
            eq(tournamentParticipants.userId, ctx.user.id)
          )
        )
        .limit(1);

      if (existing[0]) throw new Error("Already joined this tournament");

      // Add participant
      await db.insert(tournamentParticipants).values({
        tournamentId: input.tournamentId,
        userId: ctx.user.id,
      });

      // Increment participant count
      await db
        .update(tournaments)
        .set({ currentParticipants: tournament[0].currentParticipants + 1 })
        .where(eq(tournaments.id, input.tournamentId));

      return { success: true };
    }),

  updateTournamentScore: protectedProcedure
    .input(
      z.object({
        tournamentId: z.number(),
        scoreIncrease: z.number(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database not available");

      const participant = await db
        .select()
        .from(tournamentParticipants)
        .where(
          and(
            eq(tournamentParticipants.tournamentId, input.tournamentId),
            eq(tournamentParticipants.userId, ctx.user.id)
          )
        )
        .limit(1);

      if (!participant[0]) throw new Error("Not a tournament participant");

      const currentScore = typeof participant[0].score === 'string' ? parseFloat(participant[0].score) : participant[0].score;
      const newScore = currentScore + input.scoreIncrease;

      await db
        .update(tournamentParticipants)
        .set({ score: newScore.toString() })
        .where(eq(tournamentParticipants.id, participant[0].id));

      return { success: true, newScore };
    }),

  completeTournament: protectedProcedure
    .input(z.object({ tournamentId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database not available");

      // Only admin can complete tournaments
      if (ctx.user.role !== "admin") throw new Error("Unauthorized");

      const tournament = await db
        .select()
        .from(tournaments)
        .where(eq(tournaments.id, input.tournamentId))
        .limit(1);

      if (!tournament[0]) throw new Error("Tournament not found");

      // Get top participants
      const participants = await db
        .select()
        .from(tournamentParticipants)
        .where(eq(tournamentParticipants.tournamentId, input.tournamentId))
        .orderBy(desc(tournamentParticipants.score))
        .limit(10);

      // Distribute prizes based on prizeDistribution
      const prizeDistribution = tournament[0].prizeDistribution as Record<string, number>;
      const totalPrizePool = Number(tournament[0].prizePool);

      for (let i = 0; i < participants.length; i++) {
        const rank = i + 1;
        const prizePercentage = prizeDistribution[`${rank}`] || 0;
        const prizeAmount = (totalPrizePool * prizePercentage) / 100;

        if (prizeAmount > 0) {
          await db
            .update(tournamentParticipants)
            .set({ finalRank: rank, prizeWon: prizeAmount.toString() })
            .where(eq(tournamentParticipants.id, participants[i].id));
        }
      }

      // Mark tournament as completed
      await db
        .update(tournaments)
        .set({ status: "completed" })
        .where(eq(tournaments.id, input.tournamentId));

      return { success: true };
    }),
});

export type EngagementRouter = typeof engagementRouter;
