import { router, protectedProcedure } from "../_core/trpc.ts";
import { z } from "zod";
import { getDb, getOrCreateWallet } from "../db.ts";
import { dailySpins, spinHistory, wallets } from "../../drizzle/schema.ts";
import { eq } from "drizzle-orm";

// Spin wheel segments with rewards
const SPIN_SEGMENTS = [
  { id: 1, label: "0.01 SC", amount: 0.01, currency: "SC" },
  { id: 2, label: "0.05 SC", amount: 0.05, currency: "SC" },
  { id: 3, label: "0.10 SC", amount: 0.10, currency: "SC" },
  { id: 4, label: "Sorry!", amount: 0, currency: "SC" },
  { id: 5, label: "0.50 SC", amount: 0.50, currency: "SC" },
  { id: 6, label: "1.00 SC", amount: 1.00, currency: "SC" },
  { id: 7, label: "2.00 SC", amount: 2.00, currency: "SC" },
  { id: 8, label: "4.00 SC", amount: 4.00, currency: "SC" },
];

const MAX_SPINS_PER_DAY = 3;

export const dailySpinWheelRouter = router({
  // Get user's daily spin status
  getSpinStatus: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");
    const userId = ctx.user.id;

    const spinRecord = await db
      .select()
      .from(dailySpins)
      .where(eq(dailySpins.userId, userId))
      .limit(1);

    if (!spinRecord.length) {
      // Create new spin record for user
      await db.insert(dailySpins).values({
        userId,
        spinsUsed: 0,
        lastResetAt: new Date(),
        totalWon: "0.00",
      });

      return {
        spinsRemaining: MAX_SPINS_PER_DAY,
        spinsUsed: 0,
        totalWon: "0.00",
        canSpin: true,
        nextResetAt: getNextMidnight(),
      };
    }

    const record = spinRecord[0];
    const now = new Date();
    const lastReset = new Date(record.lastResetAt);
    const daysDiff = Math.floor(
      (now.getTime() - lastReset.getTime()) / (1000 * 60 * 60 * 24)
    );

    // Reset spins if a new day has passed
    if (daysDiff >= 1) {
      await db
        .update(dailySpins)
        .set({
          spinsUsed: 0,
          lastResetAt: new Date(),
        })
        .where(eq(dailySpins.userId, userId));

      return {
        spinsRemaining: MAX_SPINS_PER_DAY,
        spinsUsed: 0,
        totalWon: record.totalWon,
        canSpin: true,
        nextResetAt: getNextMidnight(),
      };
    }

    const spinsRemaining = Math.max(0, MAX_SPINS_PER_DAY - record.spinsUsed);

    return {
      spinsRemaining,
      spinsUsed: record.spinsUsed,
      totalWon: record.totalWon,
      canSpin: spinsRemaining > 0,
      nextResetAt: getNextMidnight(),
    };
  }),

  // Perform a spin
  spin: protectedProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("Database unavailable");
    const userId = ctx.user.id;

    // Check spin status
    const spinRecord = await db
      .select()
      .from(dailySpins)
      .where(eq(dailySpins.userId, userId))
      .limit(1);

    if (!spinRecord.length) {
      throw new Error("Spin record not found");
    }

    const record = spinRecord[0];
    const now = new Date();
    const lastReset = new Date(record.lastResetAt);
    const daysDiff = Math.floor(
      (now.getTime() - lastReset.getTime()) / (1000 * 60 * 60 * 24)
    );

    // Reset if new day
    let spinsUsed = record.spinsUsed;
    if (daysDiff >= 1) {
      spinsUsed = 0;
      await db
        .update(dailySpins)
        .set({
          spinsUsed: 0,
          lastResetAt: new Date(),
        })
        .where(eq(dailySpins.userId, userId));
    }

    // Check if user has spins remaining
    if (spinsUsed >= MAX_SPINS_PER_DAY) {
      throw new Error("No spins remaining today");
    }

    // Randomly select a segment
    const segmentIndex = Math.floor(Math.random() * SPIN_SEGMENTS.length);
    const segment = SPIN_SEGMENTS[segmentIndex];

    // Update spin record
    const newTotalWon = parseFloat(record.totalWon.toString()) + segment.amount;
    await db
      .update(dailySpins)
      .set({
        spinsUsed: spinsUsed + 1,
        lastSpinAt: new Date(),
        totalWon: newTotalWon.toString(),
      })
      .where(eq(dailySpins.userId, userId));

    // Award coins if not "Sorry!"
    if (segment.amount > 0) {
      // Get or create wallet
      const wallet = await db
        .select()
        .from(wallets)
        .where(eq(wallets.userId, userId))
        .limit(1);

      if (wallet.length) {
        const newBalance =
          parseFloat(wallet[0].scBalance.toString()) + segment.amount;
        await db
          .update(wallets)
          .set({
            scBalance: newBalance.toString(),
          })
          .where(eq(wallets.userId, userId));
      }
    }

    // Record in spin history
    await db.insert(spinHistory).values({
      userId,
      amount: segment.amount.toString(),
      currency: "SC",
      result: segment.label,
    });

    return {
      segmentIndex,
      segment,
      newTotalWon: newTotalWon.toString(),
      message:
        segment.amount > 0
          ? `You won ${segment.amount} SC!`
          : "Sorry, try again tomorrow!",
    };
  }),

  // Get spin history
  getHistory: protectedProcedure
    .input(z.object({ limit: z.number().default(10) }))
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");
      const userId = ctx.user.id;

      const history = await db
        .select()
        .from(spinHistory)
        .where(eq(spinHistory.userId, userId))
        .orderBy((t: any) => t.createdAt)
        .limit(input.limit);

      return history;
    }),
});

function getNextMidnight(): Date {
  const now = new Date();
  const tomorrow = new Date(now);
  tomorrow.setDate(tomorrow.getDate() + 1);
  tomorrow.setHours(0, 0, 0, 0);
  return tomorrow;
}
