/**
 * Model for CS2 Major Stage Results
 *
 * Tracks the 3-stage Swiss format of CS2 Majors:
 *   Stage 1 (Opening Stage):   16 teams, lowest-seeded, Swiss Bo1/Bo3
 *   Stage 2 (Elimination Stage): 16 teams (8 direct Challengers + 8 from Stage 1), Swiss Bo1/Bo3
 *   Stage 3 (Decider Stage):   16 teams (8 direct Legends + 8 from Stage 2), Swiss all Bo3
 *   Champions Stage:           8 teams, single-elimination (uses simple_8 bracket template)
 *
 * One row per (scoringEventId, participantId) — tracks which stage they enter,
 * where they were eliminated, their W-L record at elimination (for QP sub-ranking
 * within placements 9–16), and final placement after the event completes.
 */

import { database } from "~/database/context";
import { cs2MajorStageResults, participants } from "~/database/schema";
import { eq, and, inArray, sql } from "drizzle-orm";

export interface Cs2StageResult {
  id: string;
  scoringEventId: string;
  participantId: string;
  stageEntry: number;              // 1, 2, or 3
  stageEliminated: number | null;  // null = made Champions Stage
  stageEliminatedWins: number | null; // 0, 1, or 2 wins at time of elimination
  finalPlacement: number | null;   // 1–32, set after event completes
  createdAt: Date;
  updatedAt: Date;
}

export interface Cs2StageResultWithName extends Cs2StageResult {
  participantName: string;
}

export interface Cs2StageAssignmentInput {
  scoringEventId: string;
  participantId: string;
  stageEntry: number;
}

/**
 * Get all stage results for a CS2 Major event, joined with participant names.
 */
export async function getCs2StageResultsForEvent(
  scoringEventId: string
): Promise<Cs2StageResultWithName[]> {
  const db = database();
  const rows = await db
    .select({
      id: cs2MajorStageResults.id,
      scoringEventId: cs2MajorStageResults.scoringEventId,
      participantId: cs2MajorStageResults.participantId,
      stageEntry: cs2MajorStageResults.stageEntry,
      stageEliminated: cs2MajorStageResults.stageEliminated,
      stageEliminatedWins: cs2MajorStageResults.stageEliminatedWins,
      finalPlacement: cs2MajorStageResults.finalPlacement,
      createdAt: cs2MajorStageResults.createdAt,
      updatedAt: cs2MajorStageResults.updatedAt,
      participantName: participants.name,
    })
    .from(cs2MajorStageResults)
    .innerJoin(participants, eq(cs2MajorStageResults.participantId, participants.id))
    .where(eq(cs2MajorStageResults.scoringEventId, scoringEventId))
    .orderBy(cs2MajorStageResults.stageEntry, participants.name);

  return rows;
}

/**
 * Get raw stage results for a CS2 Major event (without participant names).
 * Used by the simulator.
 */
export async function getCs2StageResultsMapForEvent(
  scoringEventId: string
): Promise<Map<string, Cs2StageResult>> {
  const db = database();
  const rows = await db
    .select()
    .from(cs2MajorStageResults)
    .where(eq(cs2MajorStageResults.scoringEventId, scoringEventId));

  return new Map(rows.map((r) => [r.participantId, r]));
}

/**
 * Upsert stage assignments for a CS2 Major event.
 * Sets stageEntry for each participant; resets stageEliminated/stageEliminatedWins
 * so that re-assigning a team's stage also clears any previously recorded results.
 */
export async function upsertCs2StageAssignments(
  inputs: Cs2StageAssignmentInput[]
): Promise<void> {
  if (inputs.length === 0) return;
  const db = database();
  const now = new Date();

  await db
    .insert(cs2MajorStageResults)
    .values(
      inputs.map(({ scoringEventId, participantId, stageEntry }) => ({
        scoringEventId,
        participantId,
        stageEntry,
        stageEliminated: null,
        stageEliminatedWins: null,
        finalPlacement: null,
        createdAt: now,
        updatedAt: now,
      }))
    )
    .onConflictDoUpdate({
      target: [cs2MajorStageResults.scoringEventId, cs2MajorStageResults.participantId],
      set: {
        stageEntry: sql`excluded.stage_entry`,
        stageEliminated: null,
        stageEliminatedWins: null,
        finalPlacement: null,
        updatedAt: sql`excluded.updated_at`,
      },
    });
}

/**
 * Remove all stage assignments for a CS2 Major event.
 * Called when the admin resets the event setup.
 */
export async function clearCs2StageAssignments(
  scoringEventId: string
): Promise<void> {
  const db = database();
  await db
    .delete(cs2MajorStageResults)
    .where(eq(cs2MajorStageResults.scoringEventId, scoringEventId));
}

/**
 * Mark teams as eliminated from a specific stage.
 * Records stageEliminated and stageEliminatedWins for the specified participants.
 * Teams NOT in eliminatedEntries that are in this stage are implicitly considered
 * to have advanced (stageEliminated remains null).
 */
export async function markCs2StageEliminations(
  scoringEventId: string,
  eliminations: Array<{ participantId: string; winsAtElimination: number }>
): Promise<void> {
  if (eliminations.length === 0) return;
  const db = database();
  const now = new Date();

  // Use individual upserts for each eliminated team to set their specific win count
  await Promise.all(
    eliminations.map(({ participantId, winsAtElimination }) =>
      db
        .update(cs2MajorStageResults)
        .set({
          stageEliminated: sql`${cs2MajorStageResults.stageEntry}`,  // eliminated at their current stage
          stageEliminatedWins: winsAtElimination,
          updatedAt: now,
        })
        .where(and(
          eq(cs2MajorStageResults.scoringEventId, scoringEventId),
          eq(cs2MajorStageResults.participantId, participantId)
        ))
    )
  );
}

/**
 * Set final placements for all participants in a CS2 Major event.
 * Called after the Champions Stage is complete.
 * placements: Map from participantId to final placement (1–32)
 */
export async function setCs2FinalPlacements(
  scoringEventId: string,
  placements: Record<string, number>
): Promise<void> {
  const db = database();
  const now = new Date();
  const participantIds = Object.keys(placements);
  if (participantIds.length === 0) return;

  await Promise.all(
    participantIds.map((participantId) =>
      db
        .update(cs2MajorStageResults)
        .set({ finalPlacement: placements[participantId], updatedAt: now })
        .where(and(
          eq(cs2MajorStageResults.scoringEventId, scoringEventId),
          eq(cs2MajorStageResults.participantId, participantId)
        ))
    )
  );
}
