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

export type Participant = typeof schema.participants.$inferSelect;
export type NewParticipant = typeof schema.participants.$inferInsert;

export async function createParticipant(data: NewParticipant): Promise<Participant> {
  const db = database();
  const [participant] = await db
    .insert(schema.participants)
    .values(data)
    .returning();
  return participant;
}

export async function createManyParticipants(data: NewParticipant[]): Promise<Participant[]> {
  const db = database();
  return await db
    .insert(schema.participants)
    .values(data)
    .returning();
}

export async function findParticipantById(id: string): Promise<Participant | undefined> {
  const db = database();
  return await db.query.participants.findFirst({
    where: eq(schema.participants.id, id),
    with: {
      sportsSeason: {
        with: {
          sport: true,
        },
      },
    },
  });
}

export async function findParticipantsBySportsSeasonId(sportsSeasonId: string): Promise<Participant[]> {
  const db = database();
  return await db.query.participants.findMany({
    where: eq(schema.participants.sportsSeasonId, sportsSeasonId),
    orderBy: (participants, { asc }) => [asc(participants.name)],
  });
}

export async function findParticipantsByExternalId(
  externalId: string
): Promise<Participant[]> {
  const db = database();
  return await db.query.participants.findMany({
    where: eq(schema.participants.externalId, externalId),
    with: {
      sportsSeason: {
        with: {
          sport: true,
        },
      },
    },
  });
}

export async function updateParticipant(
  id: string,
  data: Partial<NewParticipant>
): Promise<Participant> {
  const db = database();
  const [participant] = await db
    .update(schema.participants)
    .set({ ...data, updatedAt: new Date() })
    .where(eq(schema.participants.id, id))
    .returning();
  return participant;
}

export async function countAllParticipants(): Promise<number> {
  const db = database();
  const result = await db.select({ value: count() }).from(schema.participants);
  return result[0].value;
}

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

export async function copyParticipantsFromSeason(
  sourceSportsSeasonId: string,
  targetSportsSeasonId: string
): Promise<Participant[]> {
  // Get all participants from source season
  const sourceParticipants = await findParticipantsBySportsSeasonId(sourceSportsSeasonId);

  // Insert them into the target season
  if (sourceParticipants.length === 0) {
    return [];
  }

  return await createManyParticipants(
    sourceParticipants.map((p) => ({
      sportsSeasonId: targetSportsSeasonId,
      name: p.name,
      shortName: p.shortName,
      externalId: p.externalId,
      expectedValue: p.expectedValue,
    }))
  );
}

/**
 * Get all participants for a season with sport information
 * Used for draft eligibility calculations
 */
export async function getParticipantsForSeasonWithSports(seasonId: string, providedDb?: ReturnType<typeof database>) {
  const db = providedDb || database();

  // First get all sports seasons for this season
  const seasonSports = await db.query.seasonSports.findMany({
    where: eq(schema.seasonSports.seasonId, seasonId),
  });

  if (seasonSports.length === 0) {
    return [];
  }

  const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId);

  // Get all participants for these sports seasons with sport info
  const participants = await db
    .select({
      id: schema.participants.id,
      name: schema.participants.name,
      sport: {
        id: schema.sports.id,
        name: schema.sports.name,
      },
    })
    .from(schema.participants)
    .innerJoin(
      schema.sportsSeasons,
      eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)
    )
    .innerJoin(
      schema.sports,
      eq(schema.sportsSeasons.sportId, schema.sports.id)
    )
    .where(
      sportsSeasonIds.length === 1
        ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0])
        : inArray(schema.participants.sportsSeasonId, sportsSeasonIds)
    );

  return participants;
}
