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

export type PlayoffMatchGame = typeof schema.playoffMatchGames.$inferSelect;
export type NewPlayoffMatchGame = typeof schema.playoffMatchGames.$inferInsert;
export type PlayoffMatchGameStatus = (typeof schema.playoffMatchGameStatusEnum.enumValues)[number];

// ─── Pure helpers ────────────────────────────────────────────────────────────

export interface SeriesScore {
  participant1Wins: number;
  participant2Wins: number;
  gamesPlayed: number;
}

/**
 * Tally wins from a list of completed games.
 * participant1Id / participant2Id on the *match* are the two series participants;
 * winnerId on each game must equal one of them.
 */
export function computeSeriesScore(
  games: Pick<PlayoffMatchGame, "winnerId" | "status">[],
  participant1Id: string,
  participant2Id: string
): SeriesScore {
  let participant1Wins = 0;
  let participant2Wins = 0;
  let gamesPlayed = 0;

  for (const game of games) {
    if (game.status !== "complete") continue;
    gamesPlayed++;
    if (game.winnerId === participant1Id) participant1Wins++;
    else if (game.winnerId === participant2Id) participant2Wins++;
  }

  return { participant1Wins, participant2Wins, gamesPlayed };
}

/**
 * Returns true when one participant has enough wins to clinch a best-of-N series.
 * e.g. best-of-7 requires 4 wins.
 */
export function isSeriesComplete(
  games: Pick<PlayoffMatchGame, "winnerId" | "status">[],
  participant1Id: string,
  participant2Id: string,
  requiredWins: number
): boolean {
  const { participant1Wins, participant2Wins } = computeSeriesScore(
    games,
    participant1Id,
    participant2Id
  );
  return participant1Wins >= requiredWins || participant2Wins >= requiredWins;
}

/**
 * Derive the series leader's participantId, or null if tied / no completed games.
 */
export function getSeriesLeader(
  games: Pick<PlayoffMatchGame, "winnerId" | "status">[],
  participant1Id: string,
  participant2Id: string
): string | null {
  const { participant1Wins, participant2Wins } = computeSeriesScore(
    games,
    participant1Id,
    participant2Id
  );
  if (participant1Wins > participant2Wins) return participant1Id;
  if (participant2Wins > participant1Wins) return participant2Id;
  return null;
}

// ─── DB operations ───────────────────────────────────────────────────────────

export async function createGame(data: NewPlayoffMatchGame): Promise<PlayoffMatchGame> {
  const db = database();
  const [game] = await db
    .insert(schema.playoffMatchGames)
    .values(data)
    .returning();
  return game;
}

export async function findGamesByMatchId(
  playoffMatchId: string
): Promise<PlayoffMatchGame[]> {
  const db = database();
  return await db.query.playoffMatchGames.findMany({
    where: eq(schema.playoffMatchGames.playoffMatchId, playoffMatchId),
    orderBy: [asc(schema.playoffMatchGames.gameNumber)],
    with: { winner: true },
  });
}

export async function findGameById(id: string): Promise<PlayoffMatchGame | undefined> {
  const db = database();
  return await db.query.playoffMatchGames.findFirst({
    where: eq(schema.playoffMatchGames.id, id),
    with: { winner: true },
  });
}

export async function updateGame(
  id: string,
  data: Partial<NewPlayoffMatchGame>
): Promise<PlayoffMatchGame | undefined> {
  const db = database();
  const [game] = await db
    .update(schema.playoffMatchGames)
    .set({ ...data, updatedAt: new Date() })
    .where(eq(schema.playoffMatchGames.id, id))
    .returning();
  return game;
}

export async function deleteGame(id: string): Promise<void> {
  const db = database();
  await db.delete(schema.playoffMatchGames).where(eq(schema.playoffMatchGames.id, id));
}

export async function deleteGamesByMatchId(playoffMatchId: string): Promise<void> {
  const db = database();
  await db
    .delete(schema.playoffMatchGames)
    .where(eq(schema.playoffMatchGames.playoffMatchId, playoffMatchId));
}
