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

export type Tournament = typeof schema.tournaments.$inferSelect;
export type NewTournament = typeof schema.tournaments.$inferInsert;
export type TournamentStatus = Tournament["status"];

export async function createTournament(
  data: NewTournament
): Promise<Tournament> {
  const db = database();
  const [tournament] = await db
    .insert(schema.tournaments)
    .values(data)
    .returning();
  return tournament;
}

export async function getTournamentById(
  id: string
): Promise<Tournament | null> {
  const db = database();
  const results = await db
    .select()
    .from(schema.tournaments)
    .where(eq(schema.tournaments.id, id))
    .limit(1);
  return results[0] ?? null;
}

export async function findTournamentsBySport(
  sportId: string
): Promise<Tournament[]> {
  const db = database();
  return await db
    .select()
    .from(schema.tournaments)
    .where(eq(schema.tournaments.sportId, sportId))
    .orderBy(asc(schema.tournaments.year), asc(schema.tournaments.startsAt));
}

export async function findTournamentBySportNameYear(
  sportId: string,
  name: string,
  year: number
): Promise<Tournament | null> {
  const db = database();
  const results = await db
    .select()
    .from(schema.tournaments)
    .where(
      and(
        eq(schema.tournaments.sportId, sportId),
        eq(schema.tournaments.name, name),
        eq(schema.tournaments.year, year)
      )
    )
    .limit(1);
  return results[0] ?? null;
}

export async function upsertTournament(
  data: NewTournament
): Promise<Tournament> {
  if (!data.sportId || !data.name || !data.year) {
    throw new Error("sportId, name, and year are required for upsert");
  }

  const existing = await findTournamentBySportNameYear(
    data.sportId,
    data.name,
    data.year
  );

  if (existing) {
    return existing;
  }

  return await createTournament(data);
}

export async function findAllTournamentsGroupedBySport(): Promise<
  Array<{
    sport: { id: string; name: string };
    tournaments: Tournament[];
  }>
> {
  const db = database();

  const rows = await db
    .select({
      tournament: schema.tournaments,
      sport: {
        id: schema.sports.id,
        name: schema.sports.name,
      },
    })
    .from(schema.tournaments)
    .innerJoin(schema.sports, eq(schema.tournaments.sportId, schema.sports.id))
    .orderBy(
      asc(schema.sports.name),
      desc(schema.tournaments.year),
      asc(schema.tournaments.startsAt)
    );

  const groupMap = new Map<
    string,
    { sport: { id: string; name: string }; tournaments: Tournament[] }
  >();

  for (const row of rows) {
    const existing = groupMap.get(row.sport.id);
    if (existing) {
      existing.tournaments.push(row.tournament);
    } else {
      groupMap.set(row.sport.id, {
        sport: row.sport,
        tournaments: [row.tournament],
      });
    }
  }

  return Array.from(groupMap.values());
}

export async function updateTournamentStatus(
  id: string,
  status: TournamentStatus
): Promise<Tournament> {
  const db = database();
  const [tournament] = await db
    .update(schema.tournaments)
    .set({ status, updatedAt: new Date() })
    .where(eq(schema.tournaments.id, id))
    .returning();
  return tournament;
}
