import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, inArray, asc } from "drizzle-orm";
import {
  getScoringRules,
  calculateFantasyPoints,
  calculateBracketPoints,
  calculateSharedPlacementPoints,
} from "./scoring-rules";

export async function createDraftPick(data: {
  seasonId: string;
  teamId: string;
  participantId: string;
  pickNumber: number;
  round: number;
  pickInRound: number;
  pickedByUserId: string;
  pickedByType: "owner" | "commissioner" | "auto";
  timeUsed: number;
}) {
  const db = database();
  const [pick] = await db.insert(schema.draftPicks).values(data).returning();
  return pick;
}

export async function getDraftPicks(seasonId: string) {
  const db = database();
  return await db
    .select()
    .from(schema.draftPicks)
    .where(eq(schema.draftPicks.seasonId, seasonId))
    .orderBy(schema.draftPicks.pickNumber);
}

export async function getDraftPickByNumber(seasonId: string, pickNumber: number) {
  const db = database();
  const [pick] = await db
    .select()
    .from(schema.draftPicks)
    .where(
      and(
        eq(schema.draftPicks.seasonId, seasonId),
        eq(schema.draftPicks.pickNumber, pickNumber)
      )
    );
  return pick;
}

export async function getTeamDraftPicks(teamId: string) {
  const db = database();
  return await db
    .select()
    .from(schema.draftPicks)
    .where(eq(schema.draftPicks.teamId, teamId))
    .orderBy(schema.draftPicks.pickNumber);
}

export async function isParticipantDrafted(seasonId: string, participantId: string, providedDb?: ReturnType<typeof database>) {
  const db = providedDb || database();
  const [pick] = await db
    .select()
    .from(schema.draftPicks)
    .where(
      and(
        eq(schema.draftPicks.seasonId, seasonId),
        eq(schema.draftPicks.participantId, participantId)
      )
    );
  return !!pick;
}

/**
 * Get a team's drafted participants grouped by sports season.
 * Returns a map of sportsSeasonId → [{id, name}].
 */
export async function getDraftedParticipantsBySportsSeason(
  teamId: string,
  seasonId: string,
  providedDb?: ReturnType<typeof database>
): Promise<Map<string, Array<{ id: string; name: string }>>> {
  const db = providedDb || database();

  const results = await db
    .select({
      sportsSeasonId: schema.seasonParticipants.sportsSeasonId,
      participantId: schema.seasonParticipants.id,
      participantName: schema.seasonParticipants.name,
    })
    .from(schema.draftPicks)
    .innerJoin(
      schema.seasonParticipants,
      eq(schema.draftPicks.participantId, schema.seasonParticipants.id)
    )
    .where(
      and(
        eq(schema.draftPicks.teamId, teamId),
        eq(schema.draftPicks.seasonId, seasonId)
      )
    );

  const map = new Map<string, Array<{ id: string; name: string }>>();
  for (const row of results) {
    if (!map.has(row.sportsSeasonId)) {
      map.set(row.sportsSeasonId, []);
    }
    map.get(row.sportsSeasonId)?.push({ id: row.participantId, name: row.participantName });
  }
  return map;
}

export interface DraftedParticipantWithPoints {
  id: string;
  name: string;
  /** Fantasy league points earned (position → scoring rules). null = no result yet. */
  earnedPoints: number | null;
  /** Accumulated qualifying points for qualifying_points sports. null for other sports. */
  currentQP: number | null;
}

/**
 * Get a team's drafted participants with their current earned points, grouped by
 * sports season. Used for the league home summary card.
 *
 * - playoff_bracket / season_standings: earnedPoints = finalPosition → scoring rules
 * - qualifying_points (active): currentQP = accumulated QP from participantQualifyingTotals
 * - qualifying_points (finalized): earnedPoints = shared finalPosition slots → scoring rules
 * - No EV / projected points — actual earned only.
 */
export async function getDraftedParticipantsWithPoints(
  teamId: string,
  seasonId: string,
  providedDb?: ReturnType<typeof database>
): Promise<Map<string, DraftedParticipantWithPoints[]>> {
  const db = providedDb || database();

  const scoringRules = await getScoringRules(seasonId, db);
  if (!scoringRules) return new Map();

  // One query: picks → participant → sportsSeason (scoringPattern) + results (position)
  const picks = await db.query.draftPicks.findMany({
    where: and(
      eq(schema.draftPicks.teamId, teamId),
      eq(schema.draftPicks.seasonId, seasonId)
    ),
    columns: {},
    with: {
      participant: {
        columns: { id: true, name: true, sportsSeasonId: true },
        with: {
          sportsSeason: { columns: { id: true, scoringPattern: true } },
          results: { columns: { finalPosition: true }, limit: 1 },
        },
      },
    },
  });

  // Collect unique sportsSeasonIds per scoring pattern
  const bracketSeasonIds = new Set<string>();
  const qpSeasonIds = new Set<string>();
  const qpParticipantIds = new Set<string>();
  const finalizedQPSeasonIds = new Set<string>();

  for (const pick of picks) {
    const pattern = pick.participant.sportsSeason.scoringPattern;
    const ssId = pick.participant.sportsSeasonId;
    if (pattern === "playoff_bracket") bracketSeasonIds.add(ssId);
    if (pattern === "qualifying_points") {
      qpSeasonIds.add(ssId);
      qpParticipantIds.add(pick.participant.id);
      if (pick.participant.results[0]?.finalPosition !== null && pick.participant.results[0]?.finalPosition !== undefined) {
        finalizedQPSeasonIds.add(ssId);
      }
    }
  }

  // Batch-fetch bracket template IDs (one per sports season)
  const bracketTemplateMap = new Map<string, string | null>();
  if (bracketSeasonIds.size > 0) {
    const events = await db.query.scoringEvents.findMany({
      where: inArray(schema.scoringEvents.sportsSeasonId, [...bracketSeasonIds]),
      columns: { sportsSeasonId: true, bracketTemplateId: true },
    });
    for (const ev of events) {
      if (!bracketTemplateMap.has(ev.sportsSeasonId)) {
        bracketTemplateMap.set(ev.sportsSeasonId, ev.bracketTemplateId ?? null);
      }
    }
  }

  // Batch-fetch QP totals for qualifying_points participants
  const qpMap = new Map<string, number>(); // participantId → totalQP
  if (qpParticipantIds.size > 0 && qpSeasonIds.size > 0) {
    const totals = await db.query.seasonParticipantQualifyingTotals.findMany({
      where: and(
        inArray(schema.seasonParticipantQualifyingTotals.participantId, [...qpParticipantIds]),
        inArray(schema.seasonParticipantQualifyingTotals.sportsSeasonId, [...qpSeasonIds])
      ),
      columns: { participantId: true, totalQualifyingPoints: true },
    });
    for (const row of totals) {
      qpMap.set(row.participantId, parseFloat(row.totalQualifyingPoints));
    }
  }

  const qpSharedPlacementCounts = new Map<string, Map<number, number>>();
  if (finalizedQPSeasonIds.size > 0) {
    const results = await db.query.seasonParticipantResults.findMany({
      where: inArray(schema.seasonParticipantResults.sportsSeasonId, [...finalizedQPSeasonIds]),
      columns: { sportsSeasonId: true, finalPosition: true },
    });
    for (const row of results) {
      if (row.finalPosition === null || row.finalPosition <= 0) continue;
      const counts = qpSharedPlacementCounts.get(row.sportsSeasonId) ?? new Map<number, number>();
      qpSharedPlacementCounts.set(row.sportsSeasonId, counts);
      counts.set(row.finalPosition, (counts.get(row.finalPosition) ?? 0) + 1);
    }
  }

  // Assemble result grouped by sportsSeasonId
  const result = new Map<string, DraftedParticipantWithPoints[]>();

  for (const pick of picks) {
    const { id, name, sportsSeasonId } = pick.participant;
    const pattern = pick.participant.sportsSeason.scoringPattern;
    const resultRow = pick.participant.results[0] ?? null;

    let earnedPoints: number | null = null;
    let currentQP: number | null = null;

    if (pattern === "qualifying_points" && (resultRow?.finalPosition === null || resultRow?.finalPosition === undefined)) {
      // Active QP season: show accumulated qualifying points
      currentQP = qpMap.get(id) ?? null;
    } else if (resultRow?.finalPosition !== null && resultRow?.finalPosition !== undefined) {
      // Finalized result for any pattern (including finalized QP seasons)
      if (pattern === "playoff_bracket") {
        earnedPoints = calculateBracketPoints(
          resultRow.finalPosition,
          scoringRules,
          bracketTemplateMap.get(sportsSeasonId) ?? null
        );
      } else if (pattern === "qualifying_points") {
        const tiedParticipants =
          qpSharedPlacementCounts.get(sportsSeasonId)?.get(resultRow.finalPosition) ?? 1;
        earnedPoints = calculateSharedPlacementPoints(
          resultRow.finalPosition,
          tiedParticipants,
          scoringRules
        );
      } else {
        earnedPoints = calculateFantasyPoints(resultRow.finalPosition, scoringRules);
      }
    }

    const arr = result.get(sportsSeasonId) ?? [];
    result.set(sportsSeasonId, arr);
    arr.push({ id, name, earnedPoints, currentQP });
  }

  return result;
}

export async function deleteAllDraftPicks(seasonId: string) {
  const db = database();
  await db
    .delete(schema.draftPicks)
    .where(eq(schema.draftPicks.seasonId, seasonId));
}

/**
 * Get all draft picks for a season with participant and sport information
 * Used for draft eligibility calculations
 */
export async function getDraftPicksWithSports(seasonId: string, providedDb?: ReturnType<typeof database>) {
  const db = providedDb || database();
  const results = await db
    .select({
      id: schema.draftPicks.id,
      teamId: schema.draftPicks.teamId,
      pickNumber: schema.draftPicks.pickNumber,
      participantId: schema.seasonParticipants.id,
      participantName: schema.seasonParticipants.name,
      sportId: schema.sports.id,
      sportName: schema.sports.name,
    })
    .from(schema.draftPicks)
    .innerJoin(
      schema.seasonParticipants,
      eq(schema.draftPicks.participantId, schema.seasonParticipants.id)
    )
    .innerJoin(
      schema.sportsSeasons,
      eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id)
    )
    .innerJoin(
      schema.sports,
      eq(schema.sportsSeasons.sportId, schema.sports.id)
    )
    .where(eq(schema.draftPicks.seasonId, seasonId))
    .orderBy(schema.draftPicks.pickNumber);

  // Transform to expected format
  return results.map((r) => ({
    teamId: r.teamId,
    participant: {
      id: r.participantId,
      sport: {
        id: r.sportId,
        name: r.sportName,
      },
    },
  }));
}

/**
 * Get team's draft picks with participant and sport information
 */
export async function getTeamDraftPicksWithSports(teamId: string, seasonId: string, providedDb?: ReturnType<typeof database>) {
  const db = providedDb || database();
  const results = await db
    .select({
      id: schema.draftPicks.id,
      teamId: schema.draftPicks.teamId,
      pickNumber: schema.draftPicks.pickNumber,
      participantId: schema.seasonParticipants.id,
      participantName: schema.seasonParticipants.name,
      sportId: schema.sports.id,
      sportName: schema.sports.name,
    })
    .from(schema.draftPicks)
    .innerJoin(
      schema.seasonParticipants,
      eq(schema.draftPicks.participantId, schema.seasonParticipants.id)
    )
    .innerJoin(
      schema.sportsSeasons,
      eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id)
    )
    .innerJoin(
      schema.sports,
      eq(schema.sportsSeasons.sportId, schema.sports.id)
    )
    .where(
      and(
        eq(schema.draftPicks.teamId, teamId),
        eq(schema.draftPicks.seasonId, seasonId)
      )
    )
    .orderBy(schema.draftPicks.pickNumber);

  // Transform to expected format
  return results.map((r) => ({
    teamId: r.teamId,
    participant: {
      id: r.participantId,
      sport: {
        id: r.sportId,
        name: r.sportName,
      },
    },
  }));
}

export async function getDraftPicksForSeason(seasonId: string) {
  const db = database();
  return await db
    .select({
      id: schema.draftPicks.id,
      pickNumber: schema.draftPicks.pickNumber,
      round: schema.draftPicks.round,
      pickInRound: schema.draftPicks.pickInRound,
      timeUsed: schema.draftPicks.timeUsed,
      team: schema.teams,
      participant: schema.seasonParticipants,
      sport: schema.sports,
    })
    .from(schema.draftPicks)
    .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id))
    .innerJoin(schema.seasonParticipants, eq(schema.draftPicks.participantId, schema.seasonParticipants.id))
    .innerJoin(schema.sportsSeasons, eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id))
    .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id))
    .where(eq(schema.draftPicks.seasonId, seasonId))
    .orderBy(asc(schema.draftPicks.pickNumber));
}
