import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId.bracket";

import { findSportsSeasonById } from "~/models/sports-season";
import { findParticipantsBySportsSeasonId } from "~/models/participant";
import { getScoringEventById, updateScoringEvent } from "~/models/scoring-event";
import {
  findPlayoffMatchesByEventId,
  generateBracketFromTemplate,
  setMatchWinner,
  advanceWinnerTemplate,
  findPlayoffMatchById,
  assignParticipantsToKnockout,
} from "~/models/playoff-match";
import {
  createGame,
  updateGame,
  deleteGame,
  type PlayoffMatchGameStatus,
} from "~/models/playoff-match-game";
import {
  upsertMatchOdds,
  deleteOddsForParticipant,
} from "~/models/playoff-match-odds";
import {
  processPlayoffEvent,
  processMatchResult,
  recalculateAffectedLeagues,
  recalculateStandings,
} from "~/models/scoring-calculator";
import { updateProbabilitiesAfterResult } from "~/services/probability-updater";
import { getBracketTemplate, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates";
import { setParticipantResult, findParticipantResultsBySportsSeasonId } from "~/models/participant-result";
import { findSeasonSportsBySportsSeasonId } from "~/models/season-sport";
import { createDailySnapshot } from "~/models/standings";
import {
  createGroupsForEvent,
  addMembersToGroup,
  findGroupsByEventId,
  toggleMemberEliminated,
  getAdvancingParticipantIds,
  getEliminatedParticipantIds,
} from "~/models/tournament-group";
import { logger } from "~/lib/logger";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq } from "drizzle-orm";

export async function loader({ params }: Route.LoaderArgs) {
  const sportsSeason = await findSportsSeasonById(params.id);

  if (!sportsSeason) {
    throw new Response("Sports season not found", { status: 404 });
  }

  const event = await getScoringEventById(params.eventId);

  if (!event) {
    throw new Response("Event not found", { status: 404 });
  }

  if (event.eventType !== "playoff_game") {
    throw new Response("This event is not a playoff event", { status: 400 });
  }

  const participants = await findParticipantsBySportsSeasonId(params.id);
  const matches = await findPlayoffMatchesByEventId(params.eventId);

  // Fetch tournament groups if this event has a group-stage template
  const tournamentGroups = await findGroupsByEventId(params.eventId);

  // The matches already include participant relations from the model query
  return {
    sportsSeason: sportsSeason as typeof sportsSeason & {
      sport: { id: string; name: string; type: string; slug: string };
    },
    event,
    participants,
    matches: matches as Array<typeof matches[0] & {
      participant1: { id: string; name: string } | null;
      participant2: { id: string; name: string } | null;
      winner: { id: string; name: string } | null;
      loser: { id: string; name: string } | null;
      games: Array<{ id: string; gameNumber: number; scheduledAt: Date | null; status: string; winner?: { name: string } | null }>;
      odds: Array<{ id: string; participantId: string; moneylineOdds: number; impliedProbability: string; oddsSource?: string | null; participant: { id: string; name: string } | null }>;
    }>,
    tournamentGroups,
  };
}

export async function action({ request, params }: Route.ActionArgs) {
  const formData = await request.formData();
  const intent = formData.get("intent");

  if (intent === "generate-bracket") {
    const templateId = formData.get("templateId");

    if (typeof templateId !== "string" || !templateId) {
      return { error: "Template ID is required" };
    }

    const template = getBracketTemplate(templateId);
    if (!template) {
      return { error: "Invalid bracket template" };
    }

    // Parse per-event region config for NCAA-style brackets
    let regionOverride: BracketRegion[] | undefined;
    if (template.regions && template.regions.length > 0) {
      const regions: BracketRegion[] = [];
      for (let r = 0; r < template.regions.length; r++) {
        const name = (formData.get(`regionName${r}`) as string | null)?.trim()
          || `Region ${r + 1}`;
        const seedsRaw = (formData.get(`regionPlayInSeeds${r}`) as string | null) || "";
        const playInSeeds = seedsRaw
          .split(",")
          .map((s) => parseInt(s.trim(), 10))
          .filter((n) => !isNaN(n) && n >= 1 && n <= 16);
        const playInSet = new Set(playInSeeds);
        regions.push({
          name,
          directSeeds: ALL_16_SEEDS.filter((s) => !playInSet.has(s)),
          playIns: playInSeeds.map((s) => ({ seedSlot: s, teams: 2 as const })),
        });
      }
      // Validate total = template.totalTeams
      const total = regions.reduce(
        (sum, r) => sum + r.directSeeds.length + r.playIns.length * 2,
        0
      );
      if (total !== template.totalTeams) {
        return {
          error: `Region config accounts for ${total} team slots but template requires ${template.totalTeams}`,
        };
      }
      regionOverride = regions;
    }

    const participantIds: string[] = [];

    for (let i = 0; i < template.totalTeams; i++) {
      const participantId = formData.get(`participant${i}`);
      if (typeof participantId !== "string" || !participantId) {
        return { error: `Participant ${i + 1} is required` };
      }
      participantIds.push(participantId);
    }

    // Check for duplicates
    const uniqueParticipants = new Set(participantIds);
    if (uniqueParticipants.size !== participantIds.length) {
      return { error: "Each participant can only be selected once" };
    }

    try {
      await generateBracketFromTemplate(params.eventId, templateId, participantIds, regionOverride);

      // PHASE 5.3: Mark participants NOT in the bracket as eliminated
      const event = await getScoringEventById(params.eventId);
      if (event) {
        // Get all participants in the sports season
        const allParticipants = await findParticipantsBySportsSeasonId(params.id);

        // Create a set of participants in the bracket for fast lookup
        const participantsInBracket = new Set(participantIds);

        // Mark participants NOT in the bracket as eliminated
        for (const participant of allParticipants) {
          if (!participantsInBracket.has(participant.id)) {
            await setParticipantResult(
              participant.id,
              event.sportsSeasonId,
              0 // 0 = didn't make playoffs, eliminated
            );
          }
        }

        logger.log(`[BracketGeneration] Marked ${allParticipants.length - participantIds.length} participants as eliminated`);
      }

      // Update the event to store the template ID, scoring start round, and region config
      await updateScoringEvent(params.eventId, {
        bracketTemplateId: templateId,
        scoringStartsAtRound: template.scoringStartsAtRound,
        bracketRegionConfig: regionOverride,
      });

      return { success: "Bracket generated successfully" };
    } catch (error) {
      logger.error("Error generating bracket:", error);
      return {
        error:
          error instanceof Error
            ? error.message
            : "Failed to generate bracket",
      };
    }
  }

  /**
   * Auto-complete a round when every match in it is marked complete.
   * Updates scoringEvents.playoffRound and calls processPlayoffEvent so that
   * non-bracket eliminations are recorded and probabilities are refreshed.
   * This replaces the manual "Complete Round" button.
   *
   * skipRecalculate=true because the caller already sent a Discord notification
   * scoped to the current batch of matches — we don't want a second one that
   * would show all previously-completed matches in the event.
   */
  async function autoCompleteRoundIfDone(
    eventId: string,
    round: string,
    sportsSeasonId: string,
    db: ReturnType<typeof database>
  ): Promise<void> {
    const allMatches = await findPlayoffMatchesByEventId(eventId);
    const roundMatches = allMatches.filter((m) => m.round === round);
    if (roundMatches.length === 0) return;
    const allDone = roundMatches.every((m) => m.isComplete);
    if (!allDone) return;

    await db
      .update(schema.scoringEvents)
      .set({ playoffRound: round, updatedAt: new Date() })
      .where(eq(schema.scoringEvents.id, eventId));

    await processPlayoffEvent(eventId, db, { skipRecalculate: true });
    logger.log(`[AutoComplete] Round "${round}" auto-completed for event ${eventId}`);
  }

  if (intent === "set-winner") {
    const matchId = formData.get("matchId");
    const winnerId = formData.get("winnerId");

    if (typeof matchId !== "string" || !matchId) {
      return { error: "Match ID is required" };
    }

    if (typeof winnerId !== "string" || !winnerId) {
      return { error: "Winner ID is required" };
    }

    try {
      const match = await findPlayoffMatchById(matchId);
      if (!match) {
        return { error: "Match not found" };
      }

      // Get the event to determine the template
      const event = await getScoringEventById(match.scoringEventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Determine loser
      const loserId =
        match.participant1Id === winnerId
          ? match.participant2Id
          : match.participant1Id;

      if (!loserId) {
        return { error: "Could not determine loser" };
      }

      // Set the winner
      await setMatchWinner(matchId, winnerId, loserId);

      // Try to advance the winner to the next round using template
      if (event.bracketTemplateId) {
        const template = getBracketTemplate(event.bracketTemplateId);
        if (template) {
          try {
            await advanceWinnerTemplate(matchId, winnerId, template);
          } catch (error) {
            // Only ignore "already filled" errors, re-throw unexpected errors
            if (
              error instanceof Error &&
              error.message.includes("already filled")
            ) {
              logger.warn("Match already filled, skipping advancement");
            } else {
              throw error; // Re-throw unexpected errors
            }
          }
        }
      }

      // Immediately score this match: loser gets their final placement,
      // winner gets provisional floor points (isPartialScore=true).
      // isScoring ?? true: default to true for legacy rows pre-dating the isScoring column.
      await processMatchResult({
        round: match.round,
        winnerId,
        loserId,
        isScoring: match.isScoring ?? true,
        sportsSeasonId: event.sportsSeasonId,
        bracketTemplateId: event.bracketTemplateId,
        eventId: event.id,
        eventName: event.name ?? undefined,
        matchId,
      });

      const db = database();
      await autoCompleteRoundIfDone(event.id, match.round, event.sportsSeasonId, db);

      return { success: "Winner set successfully" };
    } catch (error) {
      logger.error("Error setting winner:", error);
      return {
        error:
          error instanceof Error ? error.message : "Failed to set winner",
      };
    }
  }

  if (intent === "set-round-winners") {
    const round = formData.get("round");

    if (typeof round !== "string" || !round) {
      return { error: "Round is required" };
    }

    try {
      // Get all winner assignments from form data
      const winnerAssignments: Array<{ matchId: string; winnerId: string }> = [];

      for (const [key, value] of formData.entries()) {
        if (key.startsWith("winner-") && typeof value === "string") {
          const matchId = key.replace("winner-", "");
          winnerAssignments.push({ matchId, winnerId: value });
        }
      }

      if (winnerAssignments.length === 0) {
        return { error: "No winners selected" };
      }

      // Get the event to determine the template
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      const template = event.bracketTemplateId ? getBracketTemplate(event.bracketTemplateId) : null;

      // Process each winner assignment
      let successCount = 0;
      const errors: string[] = [];
      const processedMatchIds: string[] = [];

      for (const { matchId, winnerId } of winnerAssignments) {
        try {
          const match = await findPlayoffMatchById(matchId);
          if (!match) {
            errors.push(`Match ${matchId} not found`);
            continue;
          }

          // Guard: ensure the match actually belongs to the submitted round.
          if (match.round !== round) {
            errors.push(`Match ${match.matchNumber} is in round "${match.round}", not "${round}"`);
            continue;
          }

          // Determine loser
          const loserId =
            match.participant1Id === winnerId
              ? match.participant2Id
              : match.participant1Id;

          if (!loserId) {
            errors.push(`Could not determine loser for match ${match.matchNumber}`);
            continue;
          }

          // Set the winner
          await setMatchWinner(matchId, winnerId, loserId);

          // Try to advance the winner to the next round using template
          if (template) {
            try {
              await advanceWinnerTemplate(matchId, winnerId, template);
            } catch (error) {
              // Only ignore "already filled" errors
              if (
                error instanceof Error &&
                error.message.includes("already filled")
              ) {
                logger.warn("Match already filled, skipping advancement");
              } else {
                throw error;
              }
            }
          }

          // Score this match without triggering standings/probability recalc yet —
          // we batch those side effects into a single call after the loop.
          // isScoring ?? true: default to true for legacy rows pre-dating the isScoring column.
          await processMatchResult({
            round: match.round,
            winnerId,
            loserId,
            isScoring: match.isScoring ?? true,
            sportsSeasonId: event.sportsSeasonId,
            bracketTemplateId: event.bracketTemplateId,
            eventName: event.name ?? undefined,
            skipSideEffects: true,
          });

          successCount++;
          processedMatchIds.push(matchId);
        } catch (error) {
          logger.error(`Error setting winner for match ${matchId}:`, error);
          errors.push(
            `Match ${matchId}: ${error instanceof Error ? error.message : "Unknown error"}`
          );
        }
      }

      // Run side effects once for the whole batch (avoids N redundant recalcs).
      // Pass matchIds so Discord only shows the matches from this submission, not all
      // previously completed matches in the event.
      if (successCount > 0) {
        const db = database();
        await recalculateAffectedLeagues(event.sportsSeasonId, db, { eventId: event.id, eventName: event.name ?? undefined, matchIds: processedMatchIds });
        try {
          await updateProbabilitiesAfterResult(event.sportsSeasonId, true);
        } catch (error) {
          logger.error(`Error updating probabilities after batch round winners:`, error);
        }
        await autoCompleteRoundIfDone(event.id, round, event.sportsSeasonId, db);
      }

      if (errors.length > 0 && successCount === 0) {
        return { error: `Failed to set winners: ${errors.join(", ")}` };
      }

      if (errors.length > 0) {
        return {
          success: `Set ${successCount} winner(s) successfully`,
          error: `Some errors occurred: ${errors.join(", ")}`,
        };
      }

      return { success: `Successfully set ${successCount} winner(s) for ${round}` };
    } catch (error) {
      logger.error("Error setting round winners:", error);
      return {
        error:
          error instanceof Error ? error.message : "Failed to set winners",
      };
    }
  }

  if (intent === "complete-round") {
    const round = formData.get("round");

    if (typeof round !== "string" || !round) {
      return { error: "Round is required" };
    }

    try {
      // Get the event and update playoffRound to the completed round
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Verify all matches in this round are complete
      const matches = await findPlayoffMatchesByEventId(params.eventId);

      // Validate that the round exists in this event's matches
      const roundMatches = matches.filter((m) => m.round === round);
      if (roundMatches.length === 0) {
        return { error: `Round "${round}" not found in this bracket` };
      }

      const allComplete = roundMatches.every((m) => m.isComplete);
      if (!allComplete) {
        return { error: `Not all matches in ${round} are complete` };
      }

      // Validate round order: ensure previous rounds are complete
      const existingResults = await findParticipantResultsBySportsSeasonId(
        params.id
      );

      if (round === "Finals") {
        // Finals requires Semifinals to be complete (3rd/4th place results exist)
        const hasSemifinalResults = existingResults.some(
          (r) => r.finalPosition === 3 || r.finalPosition === 4
        );
        if (!hasSemifinalResults) {
          return {
            error:
              "Semifinals must be completed before Finals (no 3rd/4th place results found)",
          };
        }
      } else if (round === "Semifinals") {
        // Semifinals requires Quarterfinals to be complete if QF matches exist
        const hasQuarterfinals = matches.some((m) => m.round === "Quarterfinals");
        if (hasQuarterfinals) {
          const hasQuarterfinalsResults = existingResults.some(
            (r) =>
              r.finalPosition === 5 ||
              r.finalPosition === 6 ||
              r.finalPosition === 7 ||
              r.finalPosition === 8
          );
          if (!hasQuarterfinalsResults) {
            return {
              error:
                "Quarterfinals must be completed before Semifinals (no 5th-8th place results found)",
            };
          }
        }
      }

      // Get the sports season to find a fantasy season for scoring rules
      const sportsSeason = await findSportsSeasonById(params.id);
      if (!sportsSeason) {
        return { error: "Sports season not found" };
      }

      // Process the playoff event to calculate placements
      // TODO Phase 2.7: Refactor processPlayoffEvent to use template system
      // For now, we still need to set playoffRound for the old scoring logic
      // even though we removed it from the create event UI
      const db = database();
      await db
        .update(schema.scoringEvents)
        .set({ playoffRound: round, updatedAt: new Date() })
        .where(eq(schema.scoringEvents.id, params.eventId));

      // Get a fantasy season ID for scoring calculation
      const seasonSports = await findSeasonSportsBySportsSeasonId(params.id);
      if (seasonSports.length === 0) {
        return {
          error: "No fantasy seasons found for this sports season",
        };
      }

      // Process playoff event to update participant results
      await processPlayoffEvent(params.eventId);

      return {
        success: `${round} completed and placements calculated successfully`,
      };
    } catch (error) {
      logger.error("Error completing round:", error);
      return {
        error:
          error instanceof Error ? error.message : "Failed to complete round",
      };
    }
  }

  if (intent === "reprocess-eliminations") {
    try {
      // Get the event
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Get all matches in the bracket
      const matches = await findPlayoffMatchesByEventId(params.eventId);
      if (matches.length === 0) {
        return { error: "No bracket exists for this event" };
      }

      // Get all participants in the sports season
      const allParticipants = await findParticipantsBySportsSeasonId(params.id);

      // Collect all participant IDs that appear in ANY match
      const participantsInBracket = new Set<string>();
      for (const match of matches) {
        if (match.participant1Id) participantsInBracket.add(match.participant1Id);
        if (match.participant2Id) participantsInBracket.add(match.participant2Id);
      }

      // Mark participants NOT in the bracket as eliminated
      let eliminatedCount = 0;

      for (const participant of allParticipants) {
        if (!participantsInBracket.has(participant.id)) {
          await setParticipantResult(
            participant.id,
            event.sportsSeasonId,
            0 // 0 = didn't make playoffs, eliminated
          );
          eliminatedCount++;
        }
      }

      logger.log(`[ReprocessEliminations] Marked ${eliminatedCount} participants as eliminated`);

      return {
        success: `Successfully marked ${eliminatedCount} participant(s) as eliminated`,
      };
    } catch (error) {
      logger.error("Error reprocessing eliminations:", error);
      return {
        error:
          error instanceof Error ? error.message : "Failed to reprocess eliminations",
      };
    }
  }

  if (intent === "finalize-bracket") {
    try {
      // Get the event
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Get all matches
      const matches = await findPlayoffMatchesByEventId(params.eventId);
      if (matches.length === 0) {
        return { error: "No bracket exists for this event" };
      }

      // Get template to determine round order
      const template = event.bracketTemplateId ? getBracketTemplate(event.bracketTemplateId) : null;
      if (!template) {
        return { error: "Bracket template not found" };
      }

      // Verify ALL matches are complete
      const incompleteMatches = matches.filter((m) => !m.isComplete);
      if (incompleteMatches.length > 0) {
        return {
          error: `Cannot finalize: ${incompleteMatches.length} match(es) still incomplete`,
        };
      }

      // Get all participants in this sports season
      const allParticipants = await findParticipantsBySportsSeasonId(params.id);

      // Get participants in matches
      const participantsInMatches = new Set(
        matches.flatMap((m) => [m.participant1Id, m.participant2Id].filter(Boolean))
      );

      // Process all rounds in order
      const db = database();
      for (const round of template.rounds) {
        const roundMatches = matches.filter((m) => m.round === round.name);
        if (roundMatches.length === 0) continue;

        // Set playoffRound and process this round
        await db
          .update(schema.scoringEvents)
          .set({ playoffRound: round.name, updatedAt: new Date() })
          .where(eq(schema.scoringEvents.id, params.eventId));

        await processPlayoffEvent(params.eventId, db);
      }

      // Assign 0 points to participants not in the bracket (Q20)
      for (const participant of allParticipants) {
        if (!participantsInMatches.has(participant.id)) {
          await setParticipantResult(
            participant.id,
            params.id,
            0 // 0 placement = 0 points
          );
        }
      }

      // Mark event as complete
      await db
        .update(schema.scoringEvents)
        .set({ isComplete: true, completedAt: new Date(), updatedAt: new Date() })
        .where(eq(schema.scoringEvents.id, params.eventId));

      // Recalculate standings for all affected fantasy seasons
      const seasonSports = await findSeasonSportsBySportsSeasonId(params.id);

      for (const seasonSport of seasonSports) {
        await recalculateStandings(seasonSport.seasonId, db);
        await createDailySnapshot(seasonSport.seasonId, db);
      }

      return {
        success: `Bracket finalized! All placements calculated and standings updated.`,
      };
    } catch (error) {
      logger.error("Error finalizing bracket:", error);
      return {
        error:
          error instanceof Error ? error.message : "Failed to finalize bracket",
      };
    }
  }

  if (intent === "generate-groups") {
    const templateId = formData.get("templateId");

    if (typeof templateId !== "string" || !templateId) {
      return { error: "Template ID is required" };
    }

    const template = getBracketTemplate(templateId);
    if (!template || !template.groupStage) {
      return { error: "Invalid template or template has no group stage" };
    }

    const { groupStage } = template;
    const totalTeams = groupStage.groupCount * groupStage.teamsPerGroup;

    // Collect participant IDs from form
    const participantIds: string[] = [];
    for (let i = 0; i < totalTeams; i++) {
      const participantId = formData.get(`participant${i}`);
      if (typeof participantId !== "string" || !participantId) {
        return { error: `Participant ${i + 1} is required` };
      }
      participantIds.push(participantId);
    }

    // Check for duplicates
    const uniqueParticipants = new Set(participantIds);
    if (uniqueParticipants.size !== participantIds.length) {
      return { error: "Each participant can only be selected once" };
    }

    try {
      // Update the event with template info
      await updateScoringEvent(params.eventId, {
        bracketTemplateId: templateId,
        scoringStartsAtRound: template.scoringStartsAtRound,
      });

      // Create tournament groups
      const groups = await createGroupsForEvent(params.eventId, groupStage.groupLabels);

      // Distribute participants into groups (in selection order)
      for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) {
        const startIdx = groupIndex * groupStage.teamsPerGroup;
        const groupParticipantIds = participantIds.slice(
          startIdx,
          startIdx + groupStage.teamsPerGroup
        );
        await addMembersToGroup(groups[groupIndex].id, groupParticipantIds);
      }

      // Generate the empty knockout bracket structure
      await generateBracketFromTemplate(params.eventId, templateId);

      return { success: "Groups and knockout bracket structure created successfully" };
    } catch (error) {
      logger.error("Error generating groups:", error);
      return {
        error: error instanceof Error ? error.message : "Failed to generate groups",
      };
    }
  }

  if (intent === "toggle-elimination") {
    const memberId = formData.get("memberId");

    if (typeof memberId !== "string" || !memberId) {
      return { error: "Member ID is required" };
    }

    try {
      const updated = await toggleMemberEliminated(memberId);
      return {
        success: updated.eliminated
          ? "Team marked as eliminated"
          : "Team reinstated",
      };
    } catch (error) {
      logger.error("Error toggling elimination:", error);
      return {
        error: error instanceof Error ? error.message : "Failed to toggle elimination",
      };
    }
  }

  if (intent === "populate-knockout") {
    try {
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Parse match assignments from form
      const assignments: Array<{
        matchNumber: number;
        slot: "participant1Id" | "participant2Id";
        participantId: string;
      }> = [];

      for (const [key, value] of formData.entries()) {
        const matchPattern = /^match-(\d+)-(participant1Id|participant2Id)$/;
        const match = key.match(matchPattern);
        if (match && typeof value === "string" && value) {
          assignments.push({
            matchNumber: parseInt(match[1], 10),
            slot: match[2] as "participant1Id" | "participant2Id",
            participantId: value,
          });
        }
      }

      if (assignments.length !== 32) {
        return { error: `Expected 32 assignments but got ${assignments.length}` };
      }

      // Validate all assignments are unique participants
      const assignedParticipantIds = new Set(assignments.map((a) => a.participantId));
      if (assignedParticipantIds.size !== 32) {
        return { error: "All 32 knockout slots must have unique participants" };
      }

      // Validate all assigned participants are non-eliminated
      const advancingIds = new Set(await getAdvancingParticipantIds(params.eventId));
      for (const participantId of assignedParticipantIds) {
        if (!advancingIds.has(participantId)) {
          return { error: "All assigned participants must be non-eliminated group members" };
        }
      }

      // Assign participants to knockout bracket
      await assignParticipantsToKnockout(params.eventId, assignments);

      // Mark eliminated group participants with finalPosition = 0
      const eliminatedIds = await getEliminatedParticipantIds(params.eventId);
      for (const participantId of eliminatedIds) {
        await setParticipantResult(participantId, event.sportsSeasonId, 0);
      }

      logger.log(
        `[PopulateKnockout] Assigned 32 participants to knockout, marked ${eliminatedIds.length} as eliminated`
      );

      return { success: "Knockout bracket populated successfully" };
    } catch (error) {
      logger.error("Error populating knockout:", error);
      return {
        error: error instanceof Error ? error.message : "Failed to populate knockout",
      };
    }
  }

  // ── Game scheduling ──────────────────────────────────────────────────────

  if (intent === "add-game") {
    const matchId = formData.get("matchId");
    const gameNumber = formData.get("gameNumber");
    const scheduledAt = formData.get("scheduledAt");
    const notes = formData.get("notes");

    if (typeof matchId !== "string" || !matchId) return { error: "matchId is required" };
    if (typeof gameNumber !== "string" || !gameNumber) return { error: "gameNumber is required" };

    try {
      await createGame({
        playoffMatchId: matchId,
        gameNumber: parseInt(gameNumber, 10),
        scheduledAt: scheduledAt ? new Date(scheduledAt as string) : null,
        notes: notes ? (notes as string) : null,
      });
      return { success: "Game added" };
    } catch (error) {
      return { error: error instanceof Error ? error.message : "Failed to add game" };
    }
  }

  if (intent === "update-game") {
    const gameId = formData.get("gameId");
    const scheduledAt = formData.get("scheduledAt");
    const status = formData.get("status");
    const participant1Score = formData.get("participant1Score");
    const participant2Score = formData.get("participant2Score");
    const winnerId = formData.get("winnerId");
    const notes = formData.get("notes");

    if (typeof gameId !== "string" || !gameId) return { error: "gameId is required" };

    const validStatuses: PlayoffMatchGameStatus[] = ["scheduled", "complete", "postponed"];
    if (status && !validStatuses.includes(status as PlayoffMatchGameStatus)) {
      return { error: `Invalid status: must be one of ${validStatuses.join(", ")}` };
    }

    try {
      const updated = await updateGame(gameId, {
        ...(scheduledAt !== null && { scheduledAt: scheduledAt ? new Date(scheduledAt as string) : null }),
        ...(status && { status: status as PlayoffMatchGameStatus }),
        ...(participant1Score !== null && { participant1Score: (participant1Score as string) || null }),
        ...(participant2Score !== null && { participant2Score: (participant2Score as string) || null }),
        ...(winnerId !== null && { winnerId: (winnerId as string) || null }),
        ...(notes !== null && { notes: (notes as string) || null }),
      });
      if (!updated) return { error: "Game not found" };
      return { success: "Game updated" };
    } catch (error) {
      return { error: error instanceof Error ? error.message : "Failed to update game" };
    }
  }

  if (intent === "delete-game") {
    const gameId = formData.get("gameId");
    if (typeof gameId !== "string" || !gameId) return { error: "gameId is required" };

    try {
      await deleteGame(gameId);
      return { success: "Game deleted" };
    } catch (error) {
      return { error: error instanceof Error ? error.message : "Failed to delete game" };
    }
  }

  // ── Odds management ──────────────────────────────────────────────────────

  if (intent === "upsert-odds") {
    const matchId = formData.get("matchId");
    const participantId = formData.get("participantId");
    const moneylineOdds = formData.get("moneylineOdds");
    const oddsSource = formData.get("oddsSource");

    if (typeof matchId !== "string" || !matchId) return { error: "matchId is required" };
    if (typeof participantId !== "string" || !participantId) return { error: "participantId is required" };
    if (typeof moneylineOdds !== "string" || !moneylineOdds) return { error: "moneylineOdds is required" };

    const moneylineInt = parseInt(moneylineOdds, 10);
    if (isNaN(moneylineInt)) return { error: "moneylineOdds must be an integer" };

    try {
      await upsertMatchOdds(matchId, participantId, {
        moneylineOdds: moneylineInt,
        oddsSource: oddsSource ? (oddsSource as string) : undefined,
      });
      return { success: "Odds updated" };
    } catch (error) {
      return { error: error instanceof Error ? error.message : "Failed to update odds" };
    }
  }

  if (intent === "delete-odds") {
    const matchId = formData.get("matchId");
    const participantId = formData.get("participantId");
    if (typeof matchId !== "string" || !matchId) return { error: "matchId is required" };
    if (typeof participantId !== "string" || !participantId) return { error: "participantId is required" };

    try {
      await deleteOddsForParticipant(matchId, participantId);
      return { success: "Odds deleted" };
    } catch (error) {
      return { error: error instanceof Error ? error.message : "Failed to delete odds" };
    }
  }

  return { error: "Invalid action" };
}
