import { protectedProcedure, publicProcedure, router } from "../_core/trpc.ts";
import { z } from "zod";
import { getSportEvents, getUserBets, debitWallet, creditWallet, writeAuditLog, getOrCreateWallet, getDb } from "../db.ts";
import { eq, and } from "drizzle-orm";
import { sportBets, sportEvents } from "../../drizzle/schema.ts";

const selectionSchema = z.object({
  eventId: z.number(),
  marketType: z.string(),
  selection: z.string(),
  odds: z.number(),
});

export const sportsbookRouter = router({
  getEvents: publicProcedure
    .input(z.object({
      sport: z.string().optional(),
      status: z.string().optional(),
    }))
    .query(async ({ input }) => {
      return getSportEvents(input.sport, input.status);
    }),

  getSports: publicProcedure.query(async () => {
    return ["Football", "Basketball", "Baseball", "Hockey", "Soccer", "MMA", "Boxing", "Tennis"];
  }),

  getMyBets: protectedProcedure
    .input(z.object({ limit: z.number().default(20) }))
    .query(async ({ ctx, input }) => {
      return getUserBets(ctx.user.id, input.limit);
    }),

  placeBet: protectedProcedure
    .input(z.object({
      currency: z.enum(["GC", "SC"]),
      stake: z.number().min(0.01),
      selections: z.array(selectionSchema).min(1).max(20),
    }))
    .mutation(async ({ ctx, input }) => {
      const wallet = await getOrCreateWallet(ctx.user.id);
      const balance = parseFloat(input.currency === "GC" ? wallet.gcBalance : wallet.scBalance);
      if (balance < input.stake) throw new Error("Insufficient balance");

      // Calculate parlay odds
      const totalOdds = input.selections.reduce((acc, s) => acc * s.odds, 1);
      const potentialPayout = input.stake * totalOdds;
      const betType = input.selections.length === 1 ? "single" : "parlay";

      // Validate events exist and are bettable
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      for (const sel of input.selections) {
        const event = await db.select().from(sportEvents).where(eq(sportEvents.id, sel.eventId)).limit(1);
        if (!event[0]) throw new Error(`Event ${sel.eventId} not found`);
        if (event[0].status === "finished" || event[0].status === "cancelled") throw new Error(`Event ${event[0].homeTeam} vs ${event[0].awayTeam} is no longer available`);
      }

      // Debit stake
      await debitWallet(ctx.user.id, input.currency, input.stake, "sport_bet", `${betType === "parlay" ? "Parlay" : "Single"} bet - ${input.selections.length} selection(s)`, "sportsbook");

      // Create bet record
      await db.insert(sportBets).values({
        userId: ctx.user.id,
        currency: input.currency,
        betType,
        totalStake: input.stake.toFixed(2),
        totalOdds,
        potentialPayout: potentialPayout.toFixed(2),
        status: "pending",
        selections: JSON.stringify(input.selections),
      });

      await writeAuditLog({ actorId: ctx.user.id, actorRole: "user", action: "sport_bet_placed", category: "wallet", details: { stake: input.stake, currency: input.currency, betType, totalOdds, potentialPayout } });

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

  cancelBet: protectedProcedure
    .input(z.object({ betId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      const bet = await db.select().from(sportBets).where(and(eq(sportBets.id, input.betId), eq(sportBets.userId, ctx.user.id))).limit(1);
      if (!bet[0]) throw new Error("Bet not found");
      if (bet[0].status !== "pending") throw new Error("Only pending bets can be cancelled");

      // Check all events are still pre-match
      const selections = JSON.parse(bet[0].selections as string) as Array<{ eventId: number }>;
      for (const sel of selections) {
        const event = await db.select().from(sportEvents).where(eq(sportEvents.id, sel.eventId)).limit(1);
        if (event[0]?.status === "live") throw new Error("Cannot cancel bet on live event");
      }

      await db.update(sportBets).set({ status: "cancelled", settledAt: new Date() }).where(eq(sportBets.id, input.betId));
      await creditWallet(ctx.user.id, bet[0].currency, parseFloat(bet[0].totalStake), "sport_refund", "Bet cancelled - refund", String(input.betId));
      return { success: true };
    }),
});
