import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, max, sql } from "drizzle-orm";

export interface UpsertRegularSeasonStandingData {
  participantId: string;
  sportsSeasonId: string;
  wins: number;
  losses: number;
  otLosses?: number | null;
  ties?: number | null;
  winPct?: number | null;
  gamesPlayed: number;
  gamesBack?: number | null;
  conference?: string | null;
  division?: string | null;
  conferenceRank?: number | null;
  divisionRank?: number | null;
  leagueRank?: number | null;
  streak?: string | null;
  lastTen?: string | null;
  homeRecord?: string | null;
  awayRecord?: string | null;
  externalTeamId?: string | null;
  syncedAt?: Date | null;
}

/**
 * Bulk upsert regular season standings records.
 * Uses onConflictDoUpdate on the (participantId, sportsSeasonId) unique index.
 * Sets syncedAt = now() for auto-synced records; null means manually entered.
 */
export async function upsertRegularSeasonStandings(
  records: UpsertRegularSeasonStandingData[],
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  if (records.length === 0) return [];

  const now = new Date();
  const values = records.map((r) => ({
    participantId: r.participantId,
    sportsSeasonId: r.sportsSeasonId,
    wins: r.wins,
    losses: r.losses,
    otLosses: r.otLosses ?? null,
    ties: r.ties ?? null,
    winPct: r.winPct !== null && r.winPct !== undefined ? r.winPct.toString() : null,
    gamesPlayed: r.gamesPlayed,
    gamesBack: r.gamesBack !== null && r.gamesBack !== undefined ? r.gamesBack.toString() : null,
    conference: r.conference ?? null,
    division: r.division ?? null,
    conferenceRank: r.conferenceRank ?? null,
    divisionRank: r.divisionRank ?? null,
    leagueRank: r.leagueRank ?? null,
    streak: r.streak ?? null,
    lastTen: r.lastTen ?? null,
    homeRecord: r.homeRecord ?? null,
    awayRecord: r.awayRecord ?? null,
    externalTeamId: r.externalTeamId ?? null,
    syncedAt: r.syncedAt !== undefined ? r.syncedAt : now,
    updatedAt: now,
  }));

  return await db.transaction(async (tx) => {
    return tx
      .insert(schema.regularSeasonStandings)
      .values(values)
      .onConflictDoUpdate({
        target: [
          schema.regularSeasonStandings.participantId,
          schema.regularSeasonStandings.sportsSeasonId,
        ],
        set: {
          wins: sql`excluded.wins`,
          losses: sql`excluded.losses`,
          otLosses: sql`excluded.ot_losses`,
          ties: sql`excluded.ties`,
          winPct: sql`excluded.win_pct`,
          gamesPlayed: sql`excluded.games_played`,
          gamesBack: sql`excluded.games_back`,
          conference: sql`excluded.conference`,
          division: sql`excluded.division`,
          conferenceRank: sql`excluded.conference_rank`,
          divisionRank: sql`excluded.division_rank`,
          leagueRank: sql`excluded.league_rank`,
          streak: sql`excluded.streak`,
          lastTen: sql`excluded.last_ten`,
          homeRecord: sql`excluded.home_record`,
          awayRecord: sql`excluded.away_record`,
          externalTeamId: sql`excluded.external_team_id`,
          syncedAt: sql`excluded.synced_at`,
          updatedAt: sql`excluded.updated_at`,
        },
      })
      .returning();
  });
}

/**
 * Upsert a single standing record as a manual admin override.
 * Sets syncedAt = null to indicate manual entry (won't conflict with auto-sync).
 */
export async function upsertManualStanding(
  participantId: string,
  sportsSeasonId: string,
  data: Omit<UpsertRegularSeasonStandingData, "participantId" | "sportsSeasonId" | "syncedAt">,
  providedDb?: ReturnType<typeof database>
) {
  const results = await upsertRegularSeasonStandings(
    [{ ...data, participantId, sportsSeasonId, syncedAt: null }],
    providedDb
  );
  return results[0];
}

/**
 * Get all regular season standings for a sports season.
 * Ordered by conference, division, then divisionRank (or leagueRank as fallback).
 */
export async function getRegularSeasonStandings(
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  const rows = await db.query.regularSeasonStandings.findMany({
    where: eq(schema.regularSeasonStandings.sportsSeasonId, sportsSeasonId),
    with: {
      participant: true,
    },
  });

  // Sort: conference → division → divisionRank ?? leagueRank ?? leagueRank, nulls last
  return rows.toSorted((a, b) => {
    const confA = a.conference ?? "ZZZ";
    const confB = b.conference ?? "ZZZ";
    if (confA !== confB) return confA.localeCompare(confB);

    const divA = a.division ?? "ZZZ";
    const divB = b.division ?? "ZZZ";
    if (divA !== divB) return divA.localeCompare(divB);

    const rankA = a.divisionRank ?? a.leagueRank ?? 999;
    const rankB = b.divisionRank ?? b.leagueRank ?? 999;
    return rankA - rankB;
  });
}

/**
 * Returns the most recent syncedAt timestamp across all records for a season.
 * Returns null if no auto-synced records exist.
 */
export async function getLastSyncedAt(
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
): Promise<Date | null> {
  const db = providedDb || database();

  const result = await db
    .select({ lastSync: max(schema.regularSeasonStandings.syncedAt) })
    .from(schema.regularSeasonStandings)
    .where(eq(schema.regularSeasonStandings.sportsSeasonId, sportsSeasonId));

  return result[0]?.lastSync ?? null;
}

/**
 * Delete all regular season standings for a sports season.
 */
export async function deleteRegularSeasonStandings(
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  await db
    .delete(schema.regularSeasonStandings)
    .where(eq(schema.regularSeasonStandings.sportsSeasonId, sportsSeasonId));
}

/**
 * Get a single standing record for a participant in a season.
 */
export async function getParticipantStanding(
  participantId: string,
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  return db.query.regularSeasonStandings.findFirst({
    where: and(
      eq(schema.regularSeasonStandings.participantId, participantId),
      eq(schema.regularSeasonStandings.sportsSeasonId, sportsSeasonId)
    ),
    with: { participant: true },
  });
}
