import { getAuth } from "@clerk/react-router/server";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, sql } from "drizzle-orm";
import { isUserAdminByClerkId } from "~/models/user";
import { calculateDraftEligibility } from "~/lib/draft-eligibility";
import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick";
import { getParticipantsForSeasonWithSports } from "~/models/participant";
import { getSeasonSportsSimple } from "~/models/season-sport";
import { calculatePickInfo, checkAndTriggerNextAutodraft, pruneIneligibleQueueItems } from "~/models/draft-utils";
import { getSocketIO } from "../../../server/socket";
import { logger } from "~/lib/logger";

import type { ActionFunctionArgs } from "react-router";
export async function action(args: ActionFunctionArgs) {
  const { request } = args;
  const { userId } = await getAuth(args);

  if (!userId) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  const formData = await request.formData();
  const seasonId = formData.get("seasonId") as string;
  const participantId = formData.get("participantId") as string;

  if (!seasonId || !participantId) {
    return Response.json({ error: "Missing required fields" }, { status: 400 });
  }

  const db = database();

  // Get season details
  const season = await db.query.seasons.findFirst({
    where: eq(schema.seasons.id, seasonId),
  });

  if (!season) {
    return Response.json({ error: "Season not found" }, { status: 404 });
  }

  if (season.status !== "draft") {
    return Response.json({ error: "Draft is not currently active" }, { status: 400 });
  }

  if (season.draftPaused) {
    return Response.json({ error: "Draft is currently paused" }, { status: 400 });
  }

  // Get current draft slot (who should be picking now)
  const currentPickNumber = season.currentPickNumber || 1;
  const draftSlots = await db.query.draftSlots.findMany({
    where: eq(schema.draftSlots.seasonId, seasonId),
    orderBy: schema.draftSlots.draftOrder,
    with: {
      team: true,
    },
  });

  const totalTeams = draftSlots.length;
  const { round: currentRound, pickInRound } = calculatePickInfo(currentPickNumber, totalTeams);
  const currentDraftSlot = draftSlots.find((slot) => slot.draftOrder === pickInRound);
  
  if (!currentDraftSlot) {
    return Response.json({ error: "Invalid draft state" }, { status: 500 });
  }

  // Check permissions: must be team owner or commissioner/admin
  // Capture both results to set pickedByType accurately in the audit record
  const isTeamOwner = currentDraftSlot.team.ownerId === userId;
  const [isAdmin, commissionerRecord] = await Promise.all([
    isUserAdminByClerkId(userId),
    db.query.commissioners.findFirst({
      where: and(
        eq(schema.commissioners.leagueId, season.leagueId),
        eq(schema.commissioners.userId, userId)
      ),
    }),
  ]);

  if (!isTeamOwner && !isAdmin && !commissionerRecord) {
    return Response.json({ error: "You do not have permission to pick for this team" }, { status: 403 });
  }

  // Check if participant is already drafted
  const existingPick = await db.query.draftPicks.findFirst({
    where: and(
      eq(schema.draftPicks.seasonId, seasonId),
      eq(schema.draftPicks.participantId, participantId)
    ),
  });

  if (existingPick) {
    return Response.json({ error: "Participant already drafted" }, { status: 400 });
  }

  // Get participant details
  const participant = await db.query.participants.findFirst({
    where: eq(schema.participants.id, participantId),
    with: {
      sportsSeason: {
        with: {
          sport: true,
        },
      },
    },
  });

  if (!participant) {
    return Response.json({ error: "Participant not found" }, { status: 404 });
  }

  // ELIGIBILITY VALIDATION: Check if team can draft from this sport
  const allPicks = await getDraftPicksWithSports(seasonId);
  const teamPicks = await getTeamDraftPicksWithSports(currentDraftSlot.teamId, seasonId);
  const allParticipants = await getParticipantsForSeasonWithSports(seasonId);
  const seasonSports = await getSeasonSportsSimple(seasonId);

  // Get all teams for the season
  const allTeams = draftSlots.map((slot) => ({ id: slot.teamId }));

  const eligibility = calculateDraftEligibility(
    currentDraftSlot.teamId,
    teamPicks,
    allPicks,
    allParticipants,
    seasonSports,
    season.draftRounds,
    allTeams
  );

  const sportId = participant.sportsSeason.sport.id;
  if (!eligibility.eligibleSportIds.has(sportId)) {
    const reason = eligibility.ineligibleReasons[sportId] || "Cannot draft from this sport";
    return Response.json({ error: reason }, { status: 400 });
  }

  // Snapshot the team's time bank before the pick (used for audit / pick history)
  const timerSnapshot = await db.query.draftTimers.findFirst({
    where: and(
      eq(schema.draftTimers.seasonId, seasonId),
      eq(schema.draftTimers.teamId, currentDraftSlot.teamId)
    ),
  });

  // Create the draft pick
  const [draftPick] = await db
    .insert(schema.draftPicks)
    .values({
      seasonId,
      teamId: currentDraftSlot.teamId,
      participantId,
      pickNumber: currentPickNumber,
      round: currentRound,
      pickInRound,
      pickedByUserId: userId,
      pickedByType: isTeamOwner ? "owner" : commissionerRecord ? "commissioner" : "admin",
      timeUsed: timerSnapshot?.timeRemaining ?? 0,
    })
    .returning();

  // Remove from ALL team queues in this season (participant is now drafted)
  await db
    .delete(schema.draftQueue)
    .where(
      and(
        eq(schema.draftQueue.seasonId, seasonId),
        eq(schema.draftQueue.participantId, participantId)
      )
    );

  // Notify all clients that this participant was removed from queues
  try {
    getSocketIO().to(`draft-${seasonId}`).emit("participant-removed-from-queues", {
      participantId,
    });
  } catch (error) {
    logger.error("Socket.IO participant-removed-from-queues error:", error);
  }

  // Proactively prune queue items that are now ineligible due to this pick
  // (e.g. a team queued a snooker player but just filled their last flex slot)
  try {
    const allTeamIds = draftSlots.map((slot) => slot.teamId);
    const prunedQueues = await pruneIneligibleQueueItems({
      seasonId,
      draftRounds: season.draftRounds,
      allTeamIds,
      db,
    });
    for (const { teamId: prunedTeamId, removedParticipantIds } of prunedQueues) {
      getSocketIO().to(`draft-${seasonId}`).emit("queue-eligibility-pruned", {
        teamId: prunedTeamId,
        removedParticipantIds,
      });
    }
  } catch (error) {
    logger.error("Queue pruning error after pick:", error);
  }

  // Calculate next pick info (before updating season)
  const nextPickNumber = currentPickNumber + 1;
  const totalPicks = totalTeams * season.draftRounds;
  const isDraftComplete = nextPickNumber > totalPicks;

  // Update the picking team's timer after their pick.
  // Standard mode: always reset to the per-pick time, regardless of who picked.
  // Chess clock: always add the increment (any pick type — owner, commissioner, or admin).
  const incrementTime = season.draftIncrementTime || 30;
  let newTimeRemaining: number;

  let updatedTimer: { timeRemaining: number } | undefined;

  if (season.draftTimerMode === "standard") {
    // Atomic reset so the timer loop cannot race with this write.
    [updatedTimer] = await db
      .update(schema.draftTimers)
      .set({ timeRemaining: sql`${incrementTime}`, updatedAt: new Date() })
      .where(
        and(
          eq(schema.draftTimers.seasonId, seasonId),
          eq(schema.draftTimers.teamId, currentDraftSlot.teamId)
        )
      )
      .returning();
    newTimeRemaining = updatedTimer?.timeRemaining ?? incrementTime;
  } else {
    // Chess clock: earn the increment regardless of who made the pick (atomic add).
    [updatedTimer] = await db
      .update(schema.draftTimers)
      .set({
        timeRemaining: sql`${schema.draftTimers.timeRemaining} + ${incrementTime}`,
        updatedAt: new Date(),
      })
      .where(
        and(
          eq(schema.draftTimers.seasonId, seasonId),
          eq(schema.draftTimers.teamId, currentDraftSlot.teamId)
        )
      )
      .returning();
    newTimeRemaining = updatedTimer?.timeRemaining ?? incrementTime;
  }

  // If the timer row didn't exist yet, seed it.
  if (!updatedTimer) {
    await db.insert(schema.draftTimers).values({
      seasonId,
      teamId: currentDraftSlot.teamId,
      timeRemaining: newTimeRemaining,
    });
  }

  try {
    getSocketIO().to(`draft-${seasonId}`).emit("timer-update", {
      seasonId,
      teamId: currentDraftSlot.teamId,
      timeRemaining: newTimeRemaining,
      currentPickNumber: nextPickNumber,
    });
  } catch (error) {
    logger.error("Socket.IO timer-update error:", error);
  }

  // Next team's timer is unchanged — their bank carries forward as-is
  // (no emit needed; the timer system will start decrementing their existing bank)

  // Update season's current pick number (AFTER initializing next timer to prevent race condition)
  await db
    .update(schema.seasons)
    .set({
      currentPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
      status: isDraftComplete ? "active" : season.status,
    })
    .where(eq(schema.seasons.id, seasonId));

  // Emit socket event
  try {
    getSocketIO().to(`draft-${seasonId}`).emit("pick-made", {
      pick: {
        ...draftPick,
        team: currentDraftSlot.team,
        participant: {
          ...participant,
          sport: participant.sportsSeason.sport,
        },
        sport: participant.sportsSeason.sport,
      },
      nextPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
      isDraftComplete,
    });

    if (isDraftComplete) {
      getSocketIO().to(`draft-${seasonId}`).emit("draft-completed");
    }
  } catch (error) {
    logger.error("Socket.IO error:", error);
  }

  // Check if next team has autodraft enabled and trigger immediately
  if (!isDraftComplete) {
    const freshSeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId) });
    if (!freshSeason?.draftPaused) {
      await checkAndTriggerNextAutodraft({
        seasonId,
        nextPickNumber,
        totalTeams,
        draftSlots,
        db,
      });
    }
  }

  return Response.json({
    success: true,
    pick: draftPick,
    nextPickNumber: isDraftComplete ? currentPickNumber : nextPickNumber,
    isDraftComplete,
  });
}
