import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, notInArray, desc, inArray, sql, asc } from "drizzle-orm";
import { logger } from "~/lib/logger";
import type { InferSelectModel } from "drizzle-orm";
import { getTeamQueue, getAllQueuesForSeason } from "./draft-queue";
import { isParticipantDrafted, getDraftPicksWithSports, getTeamDraftPicksWithSports } from "./draft-pick";
import { getParticipantsForSeasonWithSports } from "./participant";
import { getSeasonSportsSimple } from "./season-sport";
import { calculateDraftEligibility } from "~/lib/draft-eligibility";
import { getSocketIO } from "../../server/socket";

/**
 * Check if the next team has autodraft enabled and immediately execute their pick
 * This is called after a pick is made to chain autodraft picks
 */
type DraftSlot = { teamId: string; draftOrder: number };

export async function checkAndTriggerNextAutodraft(params: {
  seasonId: string;
  nextPickNumber: number;
  totalTeams: number;
  draftSlots: DraftSlot[];
  db?: ReturnType<typeof database>;
}): Promise<void> {
  const { seasonId, totalTeams, draftSlots, db: providedDb } = params;
  const db = providedDb || database();

  let currentPickNumber = params.nextPickNumber;

  // Cap iterations at totalTeams: in the worst case every team has autodraft enabled,
  // so we make at most totalTeams consecutive picks before handing back to the timer loop.
  const maxIterations = params.totalTeams;
  let iterations = 0;

  // Iteratively execute autodraft picks for consecutive teams with autodraft enabled
  while (iterations < maxIterations) {
    iterations++;
    const { pickInRound: nextPickInRound } = calculatePickInfo(currentPickNumber, totalTeams);
    const nextDraftSlot = draftSlots.find((slot) => slot.draftOrder === nextPickInRound);
    if (!nextDraftSlot) return;

    const nextTeamId = nextDraftSlot.teamId;

    const autodraftSettings = await db.query.autodraftSettings.findFirst({
      where: and(
        eq(schema.autodraftSettings.seasonId, seasonId),
        eq(schema.autodraftSettings.teamId, nextTeamId)
      ),
    });

    if (!autodraftSettings?.isEnabled) return;

    logger.log(
      `[AutodraftChain] Team ${nextTeamId} has autodraft enabled, triggering immediate pick for pick ${currentPickNumber}`
    );

    const result = await executeAutoPick({
      seasonId,
      teamId: nextTeamId,
      pickNumber: currentPickNumber,
      triggeredBy: "timer",
      autodraftSettings,
      db,
      chainEnabled: false,
    });

    if (!result.success || result.isDraftComplete || !result.nextPickNumber) return;

    currentPickNumber = result.nextPickNumber;
  }
}

/**
 * Auto-pick for a team when their timer runs out
 * 1. Check queue - pick first eligible item if available (cleans up ineligible items)
 * 2. If queue empty, pick highest EV participant not drafted from eligible sports
 *
 * Updated to respect Omni league draft eligibility rules
 */
export async function autoPickForTeam(
  seasonId: string,
  teamId: string,
  draftRounds: number,
  allTeamIds: string[],
  providedDb?: ReturnType<typeof database>,
  queueOnly?: boolean
) {
  const db = providedDb || database();

  // Calculate eligibility for this team
  const allPicks = await getDraftPicksWithSports(seasonId, db);
  const teamPicks = await getTeamDraftPicksWithSports(teamId, seasonId, db);
  const allParticipants = await getParticipantsForSeasonWithSports(seasonId, db);
  const seasonSports = await getSeasonSportsSimple(seasonId, db);
  const allTeams = allTeamIds.map((id) => ({ id }));

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

  logger.log(
    `[AutoPick] Team ${teamId} eligible sports:`,
    Array.from(eligibility.eligibleSportIds)
  );

  // Check queue first - filter by eligible sports
  const queue = await getTeamQueue(teamId, db);

  if (queue.length > 0) {
    logger.log(`[AutoPick] Team ${teamId} has ${queue.length} items in queue`);

    // Get participant details for queue items to check eligibility
    const queueParticipantIds = queue.map((item) => item.participantId);
    const queueParticipants = await db.query.participants.findMany({
      where: inArray(schema.participants.id, queueParticipantIds),
      with: {
        sportsSeason: {
          with: {
            sport: true,
          },
        },
      },
    });

    const ineligibleQueueItemIds: string[] = [];

    // Try queue items in order, checking both drafted status and sport eligibility
    for (const item of queue) {
      const participant = queueParticipants.find((p) => p.id === item.participantId);
      if (!participant) {
        logger.log(`[AutoPick] Queue item ${item.id} - participant not found, will remove`);
        ineligibleQueueItemIds.push(item.id);
        continue;
      }

      const sportId = participant.sportsSeason.sport.id;
      const isEligible = eligibility.eligibleSportIds.has(sportId);
      const isDrafted = await isParticipantDrafted(seasonId, item.participantId, db);

      if (isDrafted) {
        logger.log(
          `[AutoPick] Queue item ${participant.name} (${participant.sportsSeason.sport.name}) - already drafted, will remove`
        );
        ineligibleQueueItemIds.push(item.id);
        continue;
      }

      if (!isEligible) {
        logger.log(
          `[AutoPick] Queue item ${participant.name} (${participant.sportsSeason.sport.name}) - not eligible for this team, will remove`
        );
        ineligibleQueueItemIds.push(item.id);
        continue;
      }

      // Found a valid pick from queue
      logger.log(
        `[AutoPick] Selecting from queue: ${participant.name} (${participant.sportsSeason.sport.name})`
      );

      // Clean up ineligible items from queue before returning
      if (ineligibleQueueItemIds.length > 0) {
        await db
          .delete(schema.draftQueue)
          .where(inArray(schema.draftQueue.id, ineligibleQueueItemIds));
        logger.log(`[AutoPick] Removed ${ineligibleQueueItemIds.length} ineligible items from queue`);
      }

      return item.participantId;
    }

    // All queue items were ineligible or drafted - clean them up
    if (ineligibleQueueItemIds.length > 0) {
      await db
        .delete(schema.draftQueue)
        .where(inArray(schema.draftQueue.id, ineligibleQueueItemIds));
      logger.log(
        `[AutoPick] Removed ${ineligibleQueueItemIds.length} ineligible items from queue (all items were invalid)`
      );
    }
  }

  // Queue is empty or all queued players drafted/ineligible
  if (queueOnly) {
    logger.log(`[AutoPick] No valid queue items and queueOnly constraint is active — will not fall back to highest EV`);
    return null;
  }

  // Pick highest EV available from eligible sports
  logger.log(`[AutoPick] No valid queue items, selecting highest EV from eligible sports`);
  return await getTopAvailableParticipant(seasonId, eligibility.eligibleSportIds, db);
}

/**
 * Get the highest EV participant that hasn't been drafted yet
 * Updated to filter by eligible sports
 */
export async function getTopAvailableParticipant(
  seasonId: string,
  eligibleSportIds?: Set<string>,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();
  
  // Get all drafted participant IDs
  const draftedPicks = await db
    .select({ participantId: schema.draftPicks.participantId })
    .from(schema.draftPicks)
    .where(eq(schema.draftPicks.seasonId, seasonId));
  
  const draftedIds = draftedPicks.map((p: { participantId: string }) => p.participantId);
  
  // Get all participants from season sports, filtered by eligible sports if provided
  let seasonSportsData;
  if (eligibleSportIds && eligibleSportIds.size > 0) {
    // Filter to only eligible sports
    seasonSportsData = await db
      .select({
        sportsSeasonId: schema.seasonSports.sportsSeasonId,
        sportId: schema.sports.id,
      })
      .from(schema.seasonSports)
      .innerJoin(
        schema.sportsSeasons,
        eq(schema.seasonSports.sportsSeasonId, schema.sportsSeasons.id)
      )
      .innerJoin(
        schema.sports,
        eq(schema.sportsSeasons.sportId, schema.sports.id)
      )
      .where(eq(schema.seasonSports.seasonId, seasonId));

    // Filter to only eligible sports
    seasonSportsData = seasonSportsData.filter((s: { sportId: string }) =>
      eligibleSportIds.has(s.sportId)
    );
  } else {
    // No filtering - get all sports
    seasonSportsData = await db
      .select({ sportsSeasonId: schema.seasonSports.sportsSeasonId })
      .from(schema.seasonSports)
      .where(eq(schema.seasonSports.seasonId, seasonId));
  }

  const sportsSeasonIds = seasonSportsData.map(
    (s: { sportsSeasonId: string }) => s.sportsSeasonId
  );

  if (sportsSeasonIds.length === 0) {
    return null;
  }

  // Handle multiple sports seasons: query each and sort in memory
  if (sportsSeasonIds.length > 1) {
    const allParticipants = [];

    for (const sportsSeasonId of sportsSeasonIds) {
      let participantQuery = db
        .select()
        .from(schema.participants)
        .where(eq(schema.participants.sportsSeasonId, sportsSeasonId));

      if (draftedIds.length > 0) {
        participantQuery = db
          .select()
          .from(schema.participants)
          .where(
            and(
              eq(schema.participants.sportsSeasonId, sportsSeasonId),
              notInArray(schema.participants.id, draftedIds)
            )
          );
      }

      const seasonParticipants = await participantQuery;
      allParticipants.push(...seasonParticipants);
    }

    // Sort by EV desc, then name (expectedValue is a decimal string from DB)
    allParticipants.sort((a, b) => {
      const evA = parseFloat(String(a.expectedValue)) || 0;
      const evB = parseFloat(String(b.expectedValue)) || 0;
      if (evB !== evA) {
        return evB - evA;
      }
      return a.name.localeCompare(b.name);
    });

    return allParticipants[0]?.id || null;
  }

  // Single sport season
  let query = db
    .select()
    .from(schema.participants)
    .where(eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]))
    .orderBy(desc(schema.participants.expectedValue), schema.participants.name);

  if (draftedIds.length > 0) {
    query = db
      .select()
      .from(schema.participants)
      .where(
        and(
          eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]),
          notInArray(schema.participants.id, draftedIds)
        )
      )
      .orderBy(desc(schema.participants.expectedValue), schema.participants.name);
  }

  const [topParticipant] = await query;
  return topParticipant?.id || null;
}

/**
 * Calculate the current pick based on draft order and round (snake draft).
 * Returns pickInRound that is already snake-adjusted and matches draftOrder values.
 */
export function calculatePickInfo(
  pickNumber: number,
  teamCount: number
): { round: number; pickInRound: number; teamIndex: number } {
  const round = Math.ceil(pickNumber / teamCount);
  const rawPickInRound = ((pickNumber - 1) % teamCount) + 1;

  // Snake draft: odd rounds go forward, even rounds go backward
  const isOddRound = round % 2 === 1;
  const teamIndex = isOddRound ? rawPickInRound - 1 : teamCount - rawPickInRound;
  const pickInRound = teamIndex + 1; // snake-adjusted, 1-based, matches draftOrder

  return { round, pickInRound, teamIndex };
}

/**
 * Get the team ID for a given pick number based on draft order
 */
export function getTeamForPick(
  pickNumber: number,
  draftOrder: { teamId: string; draftOrder: number }[]
): string | null {
  const sortedOrder = [...draftOrder].toSorted((a, b) => a.draftOrder - b.draftOrder);
  const teamCount = sortedOrder.length;

  if (teamCount === 0) return null;

  const { teamIndex } = calculatePickInfo(pickNumber, teamCount);
  return sortedOrder[teamIndex]?.teamId || null;
}

/**
 * After a pick is committed, recalculate draft eligibility for every team and remove
 * any queued participants whose sport is no longer eligible for that team.
 *
 * This handles cases like: a team has a snooker player queued but has now filled their
 * last flex slot that snooker could have used — the pick should be proactively removed
 * rather than silently failing or pausing the draft later.
 *
 * Returns the per-team removals so the caller can emit socket events.
 */
export async function pruneIneligibleQueueItems(params: {
  seasonId: string;
  draftRounds: number;
  allTeamIds: string[];
  db: ReturnType<typeof database>;
}): Promise<{ teamId: string; removedParticipantIds: string[] }[]> {
  const { seasonId, draftRounds, allTeamIds, db } = params;

  const [allPicks, allParticipants, seasonSports, allQueues] = await Promise.all([
    getDraftPicksWithSports(seasonId, db),
    getParticipantsForSeasonWithSports(seasonId, db),
    getSeasonSportsSimple(seasonId, db),
    getAllQueuesForSeason(seasonId, db),
  ]);

  // Build a fast lookup: participantId → sportId
  const participantSportMap = new Map<string, string>();
  for (const p of allParticipants) {
    participantSportMap.set(p.id, p.sport.id);
  }

  const allTeams = allTeamIds.map((id) => ({ id }));
  const results: { teamId: string; removedParticipantIds: string[] }[] = [];

  for (const teamId of allTeamIds) {
    const queue = allQueues.get(teamId) ?? [];
    if (queue.length === 0) continue;

    const teamPicks = allPicks.filter((p) => p.teamId === teamId);
    const eligibility = calculateDraftEligibility(
      teamId,
      teamPicks,
      allPicks,
      allParticipants,
      seasonSports,
      draftRounds,
      allTeams
    );

    const ineligible: { id: string; participantId: string }[] = [];
    for (const item of queue) {
      const sportId = participantSportMap.get(item.participantId);
      if (sportId === undefined) {
        logger.warn(
          `[QueuePrune] Team ${teamId}: queue item ${item.id} references participant ${item.participantId} not found in season sports — skipping`
        );
        continue;
      }
      if (!eligibility.eligibleSportIds.has(sportId)) {
        ineligible.push({ id: item.id, participantId: item.participantId });
      }
    }

    if (ineligible.length > 0) {
      await db
        .delete(schema.draftQueue)
        .where(inArray(schema.draftQueue.id, ineligible.map((i) => i.id)));

      const removedParticipantIds = ineligible.map((i) => i.participantId);
      results.push({ teamId, removedParticipantIds });
      logger.log(
        `[QueuePrune] Team ${teamId}: removed ${ineligible.length} ineligible items (sport no longer eligible)`
      );
    }
  }

  return results;
}

/**
 * Execute an autopick for a team - unified function for both commissioner-forced and timer-based autopicks
 *
 * Selection Logic:
 * 1. Uses the team's draft queue first (prioritizes manager's preferences)
 * 2. Validates each queued participant for:
 *    - Not already drafted
 *    - Eligible based on draft rules (sport eligibility, flex spots, etc.)
 * 3. Automatically removes ineligible participants from queue
 * 4. If queue is empty or all items are ineligible, selects highest EV participant from eligible sports
 *
 * This ensures autopicks ALWAYS respect draft eligibility rules and cannot make illegal selections.
 *
 * @param params.seasonId - The season ID
 * @param params.teamId - The team making the pick
 * @param params.pickNumber - The current pick number
 * @param params.triggeredBy - Who/what triggered the autopick ("commissioner" or "timer")
 * @param params.commissionerUserId - User ID of commissioner (required if triggeredBy is "commissioner")
 * @param params.autodraftSettings - Autodraft settings (used for timer-based picks)
 * @param params.db - Database instance (optional, will use database() if not provided)
 * @returns Result object with success status and pick data
 */
type AutodraftSettings = InferSelectModel<typeof schema.autodraftSettings>;

export async function executeAutoPick(params: {
  seasonId: string;
  teamId: string;
  pickNumber: number;
  triggeredBy: "commissioner" | "timer";
  commissionerUserId?: string;
  autodraftSettings?: AutodraftSettings | null;
  db?: ReturnType<typeof database>;
  chainEnabled?: boolean; // Set to false when called from within the autodraft chain to prevent recursion
}): Promise<{
  success: boolean;
  error?: string;
  pick?: InferSelectModel<typeof schema.draftPicks>;
  participant?: InferSelectModel<typeof schema.participants> & {
    sportsSeason: InferSelectModel<typeof schema.sportsSeasons> & {
      sport: InferSelectModel<typeof schema.sports>;
    };
  };
  nextPickNumber?: number;
  isDraftComplete?: boolean;
}> {
  const {
    seasonId,
    teamId,
    pickNumber,
    triggeredBy,
    commissionerUserId,
    autodraftSettings,
    db: providedDb,
  } = params;

  const db = providedDb || database();

  try {
    // Race condition protection - check if pick already made
    const existingPick = await db.query.draftPicks.findFirst({
      where: and(
        eq(schema.draftPicks.seasonId, seasonId),
        eq(schema.draftPicks.pickNumber, pickNumber)
      ),
    });

    if (existingPick) {
      logger.log(`[AutoPick] Pick ${pickNumber} already made, skipping`);
      return {
        success: false,
        error: "Pick already made",
      };
    }

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

    if (!season) {
      return {
        success: false,
        error: "Season not found",
      };
    }

    // Get draft slots to calculate round/pickInRound and get all team IDs
    const draftSlots = await db.query.draftSlots.findMany({
      where: eq(schema.draftSlots.seasonId, seasonId),
      orderBy: asc(schema.draftSlots.draftOrder),
      with: {
        team: true,
      },
    });

    const totalTeams = draftSlots.length;
    if (totalTeams === 0) {
      return {
        success: false,
        error: "No draft slots found",
      };
    }

    const allTeamIds = draftSlots.map((slot) => slot.teamId);

    // Use autoPickForTeam to select participant (respects eligibility and queue)
    const queueOnly = autodraftSettings?.queueOnly ?? false;
    const participantId = await autoPickForTeam(
      seasonId,
      teamId,
      season.draftRounds,
      allTeamIds,
      db,
      queueOnly
    );

    if (!participantId) {
      // If queueOnly is set and queue is empty, disable autodraft and return success
      // (timer will fire again and pick highest EV once autodraft is disabled)
      if (triggeredBy === "timer" && queueOnly && autodraftSettings) {
        logger.log(`[AutoPick] Queue empty with queueOnly constraint — disabling autodraft for team ${teamId}`);
        await db
          .update(schema.autodraftSettings)
          .set({ isEnabled: false, updatedAt: new Date() })
          .where(eq(schema.autodraftSettings.id, autodraftSettings.id));

        try {
          getSocketIO().to(`draft-${seasonId}`).emit("autodraft-updated", {
            teamId,
            isEnabled: false,
            mode: autodraftSettings.mode,
            queueOnly: autodraftSettings.queueOnly,
          });
        } catch (error) {
          logger.error("[AutoPick] Socket.IO autodraft-updated error (queue-empty shutoff):", error);
        }

        return { success: true };
      }

      return {
        success: false,
        error: "No eligible participants available to pick",
      };
    }

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

    if (!participantToPick) {
      return {
        success: false,
        error: "Participant not found",
      };
    }

    const { round: currentRound, pickInRound } = calculatePickInfo(pickNumber, totalTeams);

    // Determine pickedByUserId based on trigger
    const pickedByUserId = triggeredBy === "commissioner"
      ? (commissionerUserId || "")
      : "";

    // Fetch current timer before pick so we have the time remaining at decision point
    const incrementTime = season.draftIncrementTime || 30;
    const currentTimer = await db.query.draftTimers.findFirst({
      where: and(
        eq(schema.draftTimers.seasonId, seasonId),
        eq(schema.draftTimers.teamId, teamId)
      ),
    });

    if (!currentTimer) {
      logger.warn(`[AutoPick] No timer found for team ${teamId} in season ${seasonId}`);
    }

    // Create the draft pick
    const [draftPick] = await db
      .insert(schema.draftPicks)
      .values({
        seasonId,
        teamId,
        participantId: participantToPick.id,
        pickNumber,
        round: currentRound,
        pickInRound,
        pickedByUserId,
        pickedByType: "auto",
        // Records the team's bank balance at the moment the pick was made (seconds remaining)
        timeUsed: currentTimer ? currentTimer.timeRemaining : undefined,
      })
      .returning();

    logger.log(
      `[AutoPick] Pick created - ${triggeredBy} triggered - Pick ${pickNumber} - Participant ${participantId}`
    );

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

    // Update the team's timer after the auto-pick.
    // Standard mode: reset to the per-pick time (atomic, prevents race with timer loop).
    // Chess clock mode: add the increment so the team starts their next turn with some time
    //   (without this, a single timeout would permanently freeze their bank at 0).
    let emitTimeRemaining: number;

    if (season.draftTimerMode === "standard") {
      const [updatedTimer] = await db
        .update(schema.draftTimers)
        .set({ timeRemaining: sql`${incrementTime}`, updatedAt: new Date() })
        .where(
          and(
            eq(schema.draftTimers.seasonId, seasonId),
            eq(schema.draftTimers.teamId, teamId)
          )
        )
        .returning();
      emitTimeRemaining = updatedTimer?.timeRemaining ?? incrementTime;
      logger.log(
        `[AutoPick] Reset timer for team ${teamId} to ${emitTimeRemaining}s (standard mode)`
      );
    } else {
      // Chess clock: add the increment (atomic add, same as a manual pick).
      const [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, teamId)
          )
        )
        .returning();
      emitTimeRemaining = updatedTimer?.timeRemaining ?? incrementTime;
      if (!updatedTimer) {
        await db.insert(schema.draftTimers).values({ seasonId, teamId, timeRemaining: emitTimeRemaining });
      }
      logger.log(
        `[AutoPick] Chess clock auto-pick for team ${teamId}, bank is now ${emitTimeRemaining}s (+${incrementTime}s increment)`
      );
    }

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

    // Next team's timer is unchanged — their bank carries forward as-is

    // Update season's current pick number
    await db
      .update(schema.seasons)
      .set({
        currentPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
        status: isDraftComplete ? "active" : season.status,
      })
      .where(eq(schema.seasons.id, seasonId));

    // 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, participantToPick.id)
        )
      );

    // 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 prunedQueues = await pruneIneligibleQueueItems({
        seasonId,
        draftRounds: season.draftRounds,
        allTeamIds,
        db,
      });
      const io = getSocketIO();
      for (const { teamId: prunedTeamId, removedParticipantIds } of prunedQueues) {
        io.to(`draft-${seasonId}`).emit("queue-eligibility-pruned", {
          teamId: prunedTeamId,
          removedParticipantIds,
        });
      }
    } catch (error) {
      logger.error("[AutoPick] Error pruning ineligible queue items:", error);
    }

    // Handle autodraft settings for timer-based picks with "next_pick" mode
    if (triggeredBy === "timer" && autodraftSettings?.isEnabled && autodraftSettings.mode === "next_pick") {
      logger.log(`[AutoPick] Disabling autodraft for team ${teamId} after next_pick`);
      await db
        .update(schema.autodraftSettings)
        .set({
          isEnabled: false,
          updatedAt: new Date(),
        })
        .where(eq(schema.autodraftSettings.id, autodraftSettings.id));

      // Emit autodraft-updated event
      try {
        getSocketIO().to(`draft-${seasonId}`).emit("autodraft-updated", {
          teamId,
          isEnabled: false,
          mode: autodraftSettings.mode,
          queueOnly: autodraftSettings.queueOnly,
        });
      } catch (error) {
        logger.error("[AutoPick] Socket.IO autodraft-updated error:", error);
      }
    }

    // Emit socket events
    try {
      const io = getSocketIO();
      const team = draftSlots.find((slot) => slot.team.id === teamId)?.team;

      // Emit participant-removed-from-queues event
      io.to(`draft-${seasonId}`).emit("participant-removed-from-queues", {
        participantId: participantToPick.id,
      });

      // Emit pick-made event
      io.to(`draft-${seasonId}`).emit("pick-made", {
        pick: {
          ...draftPick,
          team,
          participant: {
            ...participantToPick,
            sport: participantToPick.sportsSeason.sport,
          },
          sport: participantToPick.sportsSeason.sport,
        },
        nextPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
        isDraftComplete,
      });

      // Emit draft-completed event if applicable
      if (isDraftComplete) {
        io.to(`draft-${seasonId}`).emit("draft-completed");
      }
    } catch (error) {
      logger.error("[AutoPick] Socket.IO events error:", error);
    }

    // Check if next team has autodraft enabled and trigger immediately
    // Only run the chain from the top-level call to prevent recursion
    if (!isDraftComplete && params.chainEnabled !== false) {
      await checkAndTriggerNextAutodraft({
        seasonId,
        nextPickNumber,
        totalTeams,
        draftSlots,
        db,
      });
    }

    return {
      success: true,
      pick: draftPick,
      participant: participantToPick,
      nextPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
      isDraftComplete,
    };
  } catch (error) {
    logger.error("[AutoPick] Error in executeAutoPick:", error);
    return {
      success: false,
      error: error instanceof Error ? error.message : "Unknown error",
    };
  }
}
