import { Badge } from "~/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "~/components/ui/table";
import { Trophy, Star } from "lucide-react";
import { TeamOwnerBadge } from "~/components/ui/team-owner-badge";

interface Participant {
  id: string;
  name: string;
}

interface Match {
  id: string;
  round: string;
  matchNumber: number;
  participant1Id: string | null;
  participant2Id: string | null;
  winnerId: string | null;
  loserId: string | null;
  isComplete: boolean;
  participant1Score: string | null;
  participant2Score: string | null;
  participant1?: Participant | null;
  participant2?: Participant | null;
  winner?: Participant | null;
  loser?: Participant | null;
}

interface TeamOwnership {
  participantId: string;
  teamName: string;
  teamId: string;
  ownerName?: string;
}

interface PlayoffBracketProps {
  matches: Match[];
  rounds: string[]; // Ordered list of round names (earliest first)
  preEliminatedParticipants?: { id: string; name: string }[]; // Eliminated before bracket (e.g. group stage)
  participantPoints?: { participantId: string; points: number }[]; // Computed fantasy points per participant
  partialScoreParticipantIds?: string[]; // Still-competing participants with provisional floor scores
  teamOwnerships?: TeamOwnership[];
  userParticipantIds?: string[];
  showOwnership?: boolean;
  title?: string;
  description?: string;
}

/** Group matches by round, sorted by matchNumber, in one pass. */
export function groupMatchesByRound(matches: Match[]): Map<string, Match[]> {
  const byRound = new Map<string, Match[]>();
  for (const match of matches) {
    if (!byRound.has(match.round)) byRound.set(match.round, []);
    byRound.get(match.round)?.push(match);
  }
  for (const group of byRound.values()) {
    group.sort((a, b) => a.matchNumber - b.matchNumber);
  }
  return byRound;
}

/**
 * For a standard single-elimination bracket, slot p1 of match N in round R
 * comes from match (2N-1) in the previous round, and slot p2 from match 2N.
 */
export function buildFeederMap(
  matchesByRound: Map<string, Match[]>,
  orderedRounds: string[]
): Map<string, { round: string; matchNumber: number }> {
  const feederMap = new Map<string, { round: string; matchNumber: number }>();

  for (let ri = 1; ri < orderedRounds.length; ri++) {
    const currentRound = orderedRounds[ri];
    const prevRound = orderedRounds[ri - 1];
    const prevMatchNums = new Set(
      (matchesByRound.get(prevRound) || []).map((m) => m.matchNumber)
    );
    for (const match of matchesByRound.get(currentRound) || []) {
      const p1Src = 2 * (match.matchNumber - 1) + 1;
      const p2Src = 2 * (match.matchNumber - 1) + 2;
      if (prevMatchNums.has(p1Src)) {
        feederMap.set(`${currentRound}:${match.matchNumber}:p1`, {
          round: prevRound,
          matchNumber: p1Src,
        });
      }
      if (prevMatchNums.has(p2Src)) {
        feederMap.set(`${currentRound}:${match.matchNumber}:p2`, {
          round: prevRound,
          matchNumber: p2Src,
        });
      }
    }
  }

  return feederMap;
}

interface EliminatedEntry {
  participant: Participant;
  score: string | null;
  rankLabel: string;
  ownership: TeamOwnership | null;
}

/**
 * For each complete match, determine which losers should be shown as eliminated
 * in that round. In double-chance brackets (e.g. AFL), a participant who lost
 * round X but then won a later round Y is NOT eliminated at round X — only their
 * final loss counts.
 *
 * Returns a map of round name → array of eliminated participant IDs.
 * Exported for unit testing.
 */
export function computeEliminatedByRound(
  matches: Array<{
    round: string;
    isComplete: boolean;
    winnerId: string | null;
    loser?: { id: string; name: string } | null;
  }>,
  rounds: string[]
): Map<string, string[]> {
  const roundIndex = new Map(rounds.map((r, i) => [r, i]));

  // Track the latest round (by index) each participant has APPEARED IN (as winner OR loser).
  // Tracking appearances — not just wins — correctly handles AFL double-chance brackets,
  // where a Qualifying Finals loser advances to Semi-Finals automatically (no intermediate
  // win required). If a participant appears in a later round, they are not yet eliminated.
  const participantLatestRound = new Map<string, number>();
  const updateLatest = (id: string, ri: number) => {
    const prev = participantLatestRound.get(id) ?? -1;
    if (ri > prev) participantLatestRound.set(id, ri);
  };
  for (const match of matches) {
    const ri = roundIndex.get(match.round) ?? -1;
    if (match.winnerId) updateLatest(match.winnerId, ri);
    if (match.loser?.id) updateLatest(match.loser.id, ri);
  }

  const result = new Map<string, string[]>();
  for (const match of matches) {
    if (!match.isComplete || !match.loser) continue;
    const lossRoundIndex = roundIndex.get(match.round) ?? -1;
    const loserLatestAppear = participantLatestRound.get(match.loser.id) ?? -1;
    // Double-chance survivor: participant appeared in a LATER round — not yet eliminated here.
    if (loserLatestAppear > lossRoundIndex) continue;
    if (!result.has(match.round)) result.set(match.round, []);
    result.get(match.round)?.push(match.loser.id);
  }
  return result;
}

export function PlayoffBracket({
  matches,
  rounds,
  preEliminatedParticipants = [],
  participantPoints = [],
  partialScoreParticipantIds: _partialScoreParticipantIds = [],
  teamOwnerships = [],
  userParticipantIds = [],
  showOwnership = true,
  title = "Playoff Bracket",
  description,
}: PlayoffBracketProps) {
  const userParticipantSet = new Set(userParticipantIds);
  const ownershipMap = new Map<string, TeamOwnership>();
  teamOwnerships.forEach((o) => ownershipMap.set(o.participantId, o));
  const pointsMap = new Map(participantPoints.map((p) => [p.participantId, p.points]));

  // Group matches once; reused for feeder map and rendering
  const matchesByRound = groupMatchesByRound(matches);
  const feederMap = buildFeederMap(matchesByRound, rounds);

  const getTbdLabel = (round: string, matchNumber: number, slot: "p1" | "p2") => {
    const feeder = feederMap.get(`${round}:${matchNumber}:${slot}`);
    if (!feeder) return "TBD";
    return `Winner of ${feeder.round} M${feeder.matchNumber}`;
  };

  // Build elimination rankings: collect losers per round, then assign rank labels.
  // In double-chance brackets (e.g. AFL), a participant may lose one round but
  // win a later one — only count them as eliminated at their FINAL losing match.
  const losersByRound = new Map<string, Array<{ participant: Participant; score: string | null; ownership: TeamOwnership | null }>>();
  let _hasScore = false;
  let bracketWinner: Participant | null = null;

  const lastRound = rounds[rounds.length - 1];
  const finalMatch = lastRound
    ? (matchesByRound.get(lastRound) || []).find((m) => m.matchNumber === 1)
    : null;
  if (finalMatch?.winner) bracketWinner = finalMatch.winner;

  // Compute which participants are eliminated in which round, handling double-chance.
  const eliminatedByRound = computeEliminatedByRound(matches, rounds);

  for (const match of matches) {
    if (!match.isComplete || !match.loser) continue;
    const eliminatedInRound = eliminatedByRound.get(match.round);
    if (!eliminatedInRound?.includes(match.loser.id)) continue;
    const loserScore =
      match.loserId === match.participant1Id
        ? match.participant1Score
        : match.participant2Score;
    if (loserScore) _hasScore = true;
    if (!losersByRound.has(match.round)) losersByRound.set(match.round, []);
    losersByRound.get(match.round)?.push({
      participant: match.loser,
      score: loserScore,
      ownership: ownershipMap.get(match.loser.id) || null,
    });
  }

  // Walk rounds latest→earliest to assign rank labels.
  // Use TOTAL match count per round (not eliminated-so-far) so that partial scoring
  // shows the correct eventual rank. E.g. R64 losers in NCAAM 68 always show T33
  // even while other R64 games are still pending.
  const allBracketParticipantIds = new Set<string>();
  for (const match of matches) {
    if (match.participant1Id) allBracketParticipantIds.add(match.participant1Id);
    if (match.participant2Id) allBracketParticipantIds.add(match.participant2Id);
  }
  const rankedEntries: EliminatedEntry[] = [];
  let nextRank = 2; // rank 1 = champion (even if not yet decided)
  for (let ri = rounds.length - 1; ri >= 0; ri--) {
    const roundName = rounds[ri];
    const roundLosers = losersByRound.get(roundName) || [];
    const totalMatchesInRound = matchesByRound.get(roundName)?.length ?? 0;
    if (roundLosers.length > 0) {
      const rankLabel = `T${nextRank}`;
      for (const loser of roundLosers) {
        rankedEntries.push({ ...loser, rankLabel });
      }
    }
    nextRank += totalMatchesInRound;
  }

  // Exclude pre-eliminated participants already ranked via bracket match losers
  const rankedParticipantIds = new Set(rankedEntries.map((e) => e.participant.id));
  if (bracketWinner) rankedParticipantIds.add(bracketWinner.id);
  const filteredPreEliminated = preEliminatedParticipants.filter(
    (p) => !rankedParticipantIds.has(p.id)
  );

  const showRankings = rankedEntries.length > 0 || bracketWinner !== null || filteredPreEliminated.length > 0;

  // Build participant lookup from match data for the "In Contention" table
  const participantMap = new Map<string, Participant>();
  for (const match of matches) {
    if (match.participant1) participantMap.set(match.participant1.id, match.participant1);
    if (match.participant2) participantMap.set(match.participant2.id, match.participant2);
  }
  const activeParticipants = [...allBracketParticipantIds]
    .filter((id) => !rankedParticipantIds.has(id))
    .map((id) => participantMap.get(id))
    .filter((p): p is Participant => p !== undefined);

  // Hide participants with 0 points that nobody drafted — they add noise without value.
  const isDraftedOrScoring = (participantId: string) =>
    ownershipMap.has(participantId) || (pointsMap.get(participantId) ?? 0) > 0;

  // Hoist winner row lookups so we don't need an IIFE in JSX
  const winnerIsOwned = bracketWinner ? userParticipantSet.has(bracketWinner.id) : false;
  const winnerOwnership = bracketWinner ? ownershipMap.get(bracketWinner.id) : undefined;
  const winnerPts = bracketWinner ? pointsMap.get(bracketWinner.id) : undefined;

  return (
    <div className="space-y-6">
      <div>
        <h2 className="text-xl font-semibold">{title}</h2>
        {description && (
          <p className="text-sm text-muted-foreground mt-1">{description}</p>
        )}
      </div>

      {matches.length === 0 ? (
        <div className="text-center py-8 text-muted-foreground">
          <p className="text-sm">No bracket matches available yet.</p>
          <p className="text-xs mt-1">
            Matches will appear here once the bracket is set up.
          </p>
        </div>
      ) : (
        <div className="space-y-8">
          {rounds.map((round) => {
            const roundMatches = matchesByRound.get(round) || [];
            if (roundMatches.length === 0) return null;

            return (
              <div key={round}>
                <div className="flex items-center gap-2 mb-3">
                  <h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
                    {round}
                  </h3>
                  <div className="flex-1 border-t border-border" />
                  <span className="text-xs text-muted-foreground">
                    {roundMatches.length}{" "}
                    {roundMatches.length === 1 ? "match" : "matches"}
                  </span>
                </div>

                <div className="grid md:grid-cols-2 gap-2">
                  {roundMatches.map((match) => {
                    const p1IsWinner =
                      match.isComplete && match.winnerId === match.participant1Id;
                    const p2IsWinner =
                      match.isComplete && match.winnerId === match.participant2Id;
                    const p1IsLoser =
                      match.isComplete && match.loserId === match.participant1Id;
                    const p2IsLoser =
                      match.isComplete && match.loserId === match.participant2Id;

                    const p1Name =
                      match.participant1?.name ||
                      (match.participant1Id
                        ? "TBD"
                        : getTbdLabel(round, match.matchNumber, "p1"));
                    const p2Name =
                      match.participant2?.name ||
                      (match.participant2Id
                        ? "TBD"
                        : getTbdLabel(round, match.matchNumber, "p2"));

                    const p1IsTbd = !match.participant1Id;
                    const p2IsTbd = !match.participant2Id;

                    const p1IsOwned = !p1IsTbd && userParticipantSet.has(match.participant1Id ?? "");
                    const p2IsOwned = !p2IsTbd && userParticipantSet.has(match.participant2Id ?? "");
                    const matchHasOwned = p1IsOwned || p2IsOwned;

                    const p1Ownership =
                      showOwnership && match.participant1Id
                        ? ownershipMap.get(match.participant1Id) || null
                        : null;
                    const p2Ownership =
                      showOwnership && match.participant2Id
                        ? ownershipMap.get(match.participant2Id) || null
                        : null;

                    return (
                      <div
                        key={match.id}
                        className={`rounded-lg border bg-card px-3 py-2 ${matchHasOwned ? "border-electric/50" : "border-border"}`}
                      >
                        {/* Match label row */}
                        <div className="flex items-center justify-between mb-2">
                          <span className="text-xs font-mono text-muted-foreground">
                            M{match.matchNumber}
                          </span>
                          {match.isComplete && (
                            <Badge variant="secondary" className="text-xs">
                              Done
                            </Badge>
                          )}
                        </div>

                        {/* Participants: left vs right (stacks on mobile) */}
                        <div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
                          {/* Participant 1 — left-aligned */}
                          <div className="flex-1 min-w-0">
                            <div className="flex items-center gap-1">
                              {p1IsOwned && !p1IsWinner && (
                                <span className="inline-block w-1.5 h-1.5 rounded-full bg-electric shrink-0" />
                              )}
                              {p1IsWinner && (
                                <Trophy className="h-3.5 w-3.5 text-yellow-500 shrink-0" />
                              )}
                              <span
                                className={[
                                  "text-sm font-medium truncate",
                                  p1IsLoser ? "text-muted-foreground line-through" : "",
                                  p1IsTbd ? "text-muted-foreground italic font-normal" : "",
                                  p1IsWinner ? "font-semibold" : "",
                                  p1IsOwned && !p1IsLoser ? "text-electric" : "",
                                ]
                                  .filter(Boolean)
                                  .join(" ")}
                              >
                                {p1Name}
                              </span>
                            </div>
                            {!p1IsTbd && p1Ownership && (
                              <div className="mt-1">
                                <TeamOwnerBadge
                                  teamName={p1Ownership.teamName}
                                  ownerName={p1Ownership.ownerName}
                                />
                              </div>
                            )}
                          </div>

                          {/* Center: scores + vs */}
                          <div className="shrink-0 flex sm:flex-col items-center gap-1 sm:gap-0.5 min-w-[2.5rem]">
                            {match.participant1Score ? (
                              <>
                                <span className="text-xs tabular-nums font-medium">
                                  {parseFloat(match.participant1Score)}
                                </span>
                                <span className="text-[10px] text-muted-foreground/60 uppercase tracking-wider">
                                  vs
                                </span>
                                <span className="text-xs tabular-nums font-medium">
                                  {match.participant2Score
                                    ? parseFloat(match.participant2Score)
                                    : "—"}
                                </span>
                              </>
                            ) : (
                              <span className="text-xs text-muted-foreground">vs</span>
                            )}
                          </div>

                          {/* Participant 2 — right-aligned on sm+, left-aligned on mobile */}
                          <div className="flex-1 min-w-0 flex flex-col sm:items-end">
                            <div className="flex items-center gap-1 sm:justify-end">
                              <span
                                className={[
                                  "text-sm font-medium truncate",
                                  p2IsLoser ? "text-muted-foreground line-through" : "",
                                  p2IsTbd ? "text-muted-foreground italic font-normal" : "",
                                  p2IsWinner ? "font-semibold" : "",
                                  p2IsOwned && !p2IsLoser ? "text-electric" : "",
                                ]
                                  .filter(Boolean)
                                  .join(" ")}
                              >
                                {p2Name}
                              </span>
                              {p2IsWinner && (
                                <Trophy className="h-3.5 w-3.5 text-yellow-500 shrink-0" />
                              )}
                              {p2IsOwned && !p2IsWinner && (
                                <span className="inline-block w-1.5 h-1.5 rounded-full bg-electric shrink-0" />
                              )}
                            </div>
                            {!p2IsTbd && p2Ownership && (
                              <div className="mt-1">
                                <TeamOwnerBadge
                                  teamName={p2Ownership.teamName}
                                  ownerName={p2Ownership.ownerName}
                                />
                              </div>
                            )}
                          </div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            );
          })}

          {/* In Contention */}
          {activeParticipants.length > 0 && (
            <Card className="border-green-500/30">
              <CardHeader>
                <CardTitle className="text-green-600 dark:text-green-400 flex items-center gap-2">
                  <Star className="h-5 w-5" />
                  In Contention
                </CardTitle>
              </CardHeader>
              <CardContent>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead>Participant</TableHead>
                      {showOwnership && (
                        <TableHead className="w-40 pl-6">Drafted By</TableHead>
                      )}
                      {pointsMap.size > 0 && (
                        <TableHead className="text-right w-16">Pts</TableHead>
                      )}
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {activeParticipants.map((p) => {
                      const isOwned = userParticipantSet.has(p.id);
                      const ownership = showOwnership ? ownershipMap.get(p.id) || null : null;
                      const pts = pointsMap.get(p.id);
                      return (
                        <TableRow
                          key={p.id}
                          className={isOwned ? "bg-electric/5 border-l-2 border-l-electric" : ""}
                        >
                          <TableCell className={isOwned ? "text-electric font-medium" : ""}>
                            {p.name}
                            {isOwned && (
                              <Star className="inline ml-1.5 h-3 w-3 fill-current text-electric" />
                            )}
                          </TableCell>
                          {showOwnership && (
                            <TableCell className="pl-6">
                              {ownership ? (
                                <TeamOwnerBadge teamName={ownership.teamName} ownerName={ownership.ownerName} />
                              ) : (
                                <span className="text-xs text-muted-foreground">-</span>
                              )}
                            </TableCell>
                          )}
                          {pointsMap.size > 0 && (
                            <TableCell className="text-right font-mono text-sm">
                              {pts !== undefined ? pts : "–"}
                            </TableCell>
                          )}
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </CardContent>
            </Card>
          )}

          {/* Final Rankings / Eliminated Teams */}
          {showRankings && (
            <Card className="border-electric/30">
              <CardHeader>
                <CardTitle className="text-electric flex items-center gap-2">
                  <Trophy className="h-5 w-5" />
                  {bracketWinner ? "Final Rankings" : "Eliminated Teams"}
                </CardTitle>
              </CardHeader>
              <CardContent>
                <Table>
                  <TableHeader>
                    <TableRow>
                      <TableHead className="w-20">Rank</TableHead>
                      <TableHead>Participant</TableHead>
                      {showOwnership && (
                        <TableHead className="w-40 pl-6">Drafted By</TableHead>
                      )}
                      {pointsMap.size > 0 && (
                        <TableHead className="text-right w-16">Pts</TableHead>
                      )}
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {bracketWinner && (
                      <TableRow className={winnerIsOwned ? "bg-electric/8 border-l-2 border-l-electric" : "bg-yellow-50/30 dark:bg-yellow-950/10"}>
                        <TableCell className="font-semibold text-yellow-600 dark:text-yellow-400">
                          <span className="flex items-center gap-1">
                            <Trophy className="h-3 w-3" />
                            #1
                          </span>
                        </TableCell>
                        <TableCell className="font-semibold">
                          {bracketWinner.name}
                          {winnerIsOwned && (
                            <Star className="inline ml-1.5 h-3 w-3 fill-current text-electric" />
                          )}
                        </TableCell>
                        {showOwnership && (
                          <TableCell className="pl-6">
                            {winnerOwnership ? (
                              <TeamOwnerBadge teamName={winnerOwnership.teamName} ownerName={winnerOwnership.ownerName} />
                            ) : (
                              <span className="text-xs text-muted-foreground">-</span>
                            )}
                          </TableCell>
                        )}
                        {pointsMap.size > 0 && (
                          <TableCell className="text-right font-semibold text-electric">
                            {winnerPts ?? "—"}
                          </TableCell>
                        )}
                      </TableRow>
                    )}

                    {rankedEntries.filter((entry) => isDraftedOrScoring(entry.participant.id)).map((entry) => {
                      const isOwned = userParticipantSet.has(entry.participant.id);
                      const pts = pointsMap.get(entry.participant.id);
                      return (
                        <TableRow
                          key={entry.participant.id}
                          className={isOwned ? "bg-electric/5 border-l-2 border-l-electric opacity-80" : "opacity-60"}
                        >
                          <TableCell className="font-mono text-xs text-muted-foreground">
                            {entry.rankLabel}
                          </TableCell>
                          <TableCell className={isOwned ? "text-electric font-medium" : "text-muted-foreground"}>
                            {entry.participant.name}
                            {isOwned && (
                              <Star className="inline ml-1.5 h-3 w-3 fill-current text-electric" />
                            )}
                          </TableCell>
                          {showOwnership && (
                            <TableCell className="pl-6">
                              {entry.ownership ? (
                                <TeamOwnerBadge
                                  teamName={entry.ownership.teamName}
                                  ownerName={entry.ownership.ownerName}
                                />
                              ) : (
                                <span className="text-xs text-muted-foreground">-</span>
                              )}
                            </TableCell>
                          )}
                          {pointsMap.size > 0 && (
                            <TableCell className="text-right tabular-nums text-muted-foreground">
                              {pts ?? "—"}
                            </TableCell>
                          )}
                        </TableRow>
                      );
                    })}

                    {filteredPreEliminated.filter((p) => isDraftedOrScoring(p.id)).map((p) => {
                      const isOwned = userParticipantSet.has(p.id);
                      const ownership = showOwnership ? ownershipMap.get(p.id) || null : null;
                      const pts = pointsMap.get(p.id);
                      return (
                        <TableRow
                          key={p.id}
                          className={isOwned ? "bg-electric/5 border-l-2 border-l-electric opacity-60" : "opacity-40"}
                        >
                          <TableCell className="font-mono text-xs text-muted-foreground">—</TableCell>
                          <TableCell className={isOwned ? "text-electric font-medium" : "text-muted-foreground"}>
                            {p.name}
                            {isOwned && (
                              <Star className="inline ml-1.5 h-3 w-3 fill-current text-electric" />
                            )}
                          </TableCell>
                          {showOwnership && (
                            <TableCell className="pl-6">
                              {ownership ? (
                                <TeamOwnerBadge teamName={ownership.teamName} ownerName={ownership.ownerName} />
                              ) : (
                                <span className="text-xs text-muted-foreground">-</span>
                              )}
                            </TableCell>
                          )}
                          {pointsMap.size > 0 && (
                            <TableCell className="text-right tabular-nums text-muted-foreground">
                              {pts ?? "—"}
                            </TableCell>
                          )}
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </CardContent>
            </Card>
          )}
        </div>
      )}
    </div>
  );
}
