/**
 * Model for Participant Surface Elos
 *
 * Manages surface-specific Elo ratings for tennis (and future surface-based sports).
 * Each participant in a sports season can have separate Elo ratings for hard,
 * clay, and grass courts.
 */

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

export type CourtSurface = "hard" | "clay" | "grass";

export interface SurfaceEloRecord {
  id: string;
  participantId: string;
  sportsSeasonId: string;
  worldRanking: number | null;
  eloHard: number | null;
  eloClay: number | null;
  eloGrass: number | null;
  updatedAt: Date;
}

export interface SurfaceEloWithName extends SurfaceEloRecord {
  participantName: string;
}

export interface SurfaceEloInput {
  participantId: string;
  sportsSeasonId: string;
  worldRanking?: number | null;
  eloHard?: number | null;
  eloClay?: number | null;
  eloGrass?: number | null;
}

/**
 * Get all surface Elo records for a sports season, joined with participant names.
 * Returns one record per participant (participants with no Elo record are excluded).
 */
export async function getSurfaceElosForSeason(
  sportsSeasonId: string
): Promise<SurfaceEloWithName[]> {
  const db = database();
  const rows = await db
    .select({
      id: participantSurfaceElos.id,
      participantId: participantSurfaceElos.participantId,
      sportsSeasonId: participantSurfaceElos.sportsSeasonId,
      worldRanking: participantSurfaceElos.worldRanking,
      eloHard: participantSurfaceElos.eloHard,
      eloClay: participantSurfaceElos.eloClay,
      eloGrass: participantSurfaceElos.eloGrass,
      updatedAt: participantSurfaceElos.updatedAt,
      participantName: participants.name,
    })
    .from(participantSurfaceElos)
    .innerJoin(participants, eq(participantSurfaceElos.participantId, participants.id))
    .where(eq(participantSurfaceElos.sportsSeasonId, sportsSeasonId))
    .orderBy(participants.name);

  return rows;
}

/**
 * Upsert surface Elo ratings for a batch of participants.
 * Uses INSERT ... ON CONFLICT DO UPDATE so all three surface columns are
 * overwritten atomically — the admin always submits all three values.
 */
export async function batchUpsertSurfaceElos(
  inputs: SurfaceEloInput[]
): Promise<void> {
  if (inputs.length === 0) return;
  const db = database();
  const now = new Date();

  await db
    .insert(participantSurfaceElos)
    .values(
      inputs.map(({ participantId, sportsSeasonId, worldRanking, eloHard, eloClay, eloGrass }) => ({
        participantId,
        sportsSeasonId,
        worldRanking: worldRanking ?? null,
        eloHard: eloHard ?? null,
        eloClay: eloClay ?? null,
        eloGrass: eloGrass ?? null,
        updatedAt: now,
      }))
    )
    .onConflictDoUpdate({
      target: [participantSurfaceElos.participantId, participantSurfaceElos.sportsSeasonId],
      set: {
        worldRanking: sql`excluded.world_ranking`,
        eloHard: sql`excluded.elo_hard`,
        eloClay: sql`excluded.elo_clay`,
        eloGrass: sql`excluded.elo_grass`,
        updatedAt: sql`excluded.updated_at`,
      },
    });
}

/**
 * Returns a Map from participantId to surface Elos for use in the simulator.
 * Participants with no record are absent from the map (simulator falls back to 1500).
 */
export async function getSurfaceEloMap(
  sportsSeasonId: string
): Promise<Map<string, { worldRanking: number | null; eloHard: number | null; eloClay: number | null; eloGrass: number | null }>> {
  const db = database();
  const rows = await db
    .select({
      participantId: participantSurfaceElos.participantId,
      worldRanking: participantSurfaceElos.worldRanking,
      eloHard: participantSurfaceElos.eloHard,
      eloClay: participantSurfaceElos.eloClay,
      eloGrass: participantSurfaceElos.eloGrass,
    })
    .from(participantSurfaceElos)
    .where(eq(participantSurfaceElos.sportsSeasonId, sportsSeasonId));

  return new Map(rows.map((r) => [r.participantId, {
    worldRanking: r.worldRanking,
    eloHard: r.eloHard,
    eloClay: r.eloClay,
    eloGrass: r.eloGrass,
  }]));
}
