import { describe, it, expect } from "vitest";
import {
  NCAA_68,
  getScoringRoundType,
  buildNCAA68SlotMap,
  matchIndexForSeedSlot,
  STANDARD_BRACKET_SEEDING,
} from "~/lib/bracket-templates";

/**
 * NCAA 68 Bracket Unit Tests - Phase 2.8
 *
 * Tests the NCAA 68 tournament template structure and scoring logic:
 * - 68 teams total (60 direct seeds + 8 in First Four)
 * - First Four: 4 play-in games (8 teams)
 * - Round of 64: 32 games
 * - Only Elite Eight and beyond score fantasy points
 */
describe("NCAA 68 Bracket Template - Phase 2.8", () => {
  describe("Template Structure", () => {
    it("has correct total teams", () => {
      expect(NCAA_68.totalTeams).toBe(68);
    });

    it("has all 7 rounds", () => {
      expect(NCAA_68.rounds).toHaveLength(7);
      expect(NCAA_68.rounds[0].name).toBe("First Four");
      expect(NCAA_68.rounds[1].name).toBe("Round of 64");
      expect(NCAA_68.rounds[2].name).toBe("Round of 32");
      expect(NCAA_68.rounds[3].name).toBe("Sweet Sixteen");
      expect(NCAA_68.rounds[4].name).toBe("Elite Eight");
      expect(NCAA_68.rounds[5].name).toBe("Final Four");
      expect(NCAA_68.rounds[6].name).toBe("Championship");
    });

    it("has correct match counts per round", () => {
      expect(NCAA_68.rounds[0].matchCount).toBe(4); // First Four
      expect(NCAA_68.rounds[1].matchCount).toBe(32); // Round of 64
      expect(NCAA_68.rounds[2].matchCount).toBe(16); // Round of 32
      expect(NCAA_68.rounds[3].matchCount).toBe(8); // Sweet Sixteen
      expect(NCAA_68.rounds[4].matchCount).toBe(4); // Elite Eight (Quarterfinals)
      expect(NCAA_68.rounds[5].matchCount).toBe(2); // Final Four (Semifinals)
      expect(NCAA_68.rounds[6].matchCount).toBe(1); // Championship (Finals)
    });

    it("has correct round feeding structure", () => {
      expect(NCAA_68.rounds[0].feedsInto).toBe("Round of 64");
      expect(NCAA_68.rounds[1].feedsInto).toBe("Round of 32");
      expect(NCAA_68.rounds[2].feedsInto).toBe("Sweet Sixteen");
      expect(NCAA_68.rounds[3].feedsInto).toBe("Elite Eight");
      expect(NCAA_68.rounds[4].feedsInto).toBe("Final Four");
      expect(NCAA_68.rounds[5].feedsInto).toBe("Championship");
      expect(NCAA_68.rounds[6].feedsInto).toBeNull(); // Championship has no next round
    });
  });

  describe("Scoring Configuration", () => {
    it("marks scoring to start at Elite Eight", () => {
      expect(NCAA_68.scoringStartsAtRound).toBe("Elite Eight");
    });

    it("marks First Four as non-scoring", () => {
      const firstFour = NCAA_68.rounds[0];
      expect(firstFour.name).toBe("First Four");
      expect(firstFour.isScoring).toBe(false);
    });

    it("marks Round of 64 as non-scoring", () => {
      const roundOf64 = NCAA_68.rounds[1];
      expect(roundOf64.name).toBe("Round of 64");
      expect(roundOf64.isScoring).toBe(false);
    });

    it("marks Round of 32 as non-scoring", () => {
      const roundOf32 = NCAA_68.rounds[2];
      expect(roundOf32.name).toBe("Round of 32");
      expect(roundOf32.isScoring).toBe(false);
    });

    it("marks Sweet Sixteen as non-scoring", () => {
      const sweetSixteen = NCAA_68.rounds[3];
      expect(sweetSixteen.name).toBe("Sweet Sixteen");
      expect(sweetSixteen.isScoring).toBe(false);
    });

    it("marks Elite Eight as scoring (Quarterfinals)", () => {
      const eliteEight = NCAA_68.rounds[4];
      expect(eliteEight.name).toBe("Elite Eight");
      expect(eliteEight.isScoring).toBe(true);
    });

    it("marks Final Four as scoring (Semifinals)", () => {
      const finalFour = NCAA_68.rounds[5];
      expect(finalFour.name).toBe("Final Four");
      expect(finalFour.isScoring).toBe(true);
    });

    it("marks Championship as scoring (Finals)", () => {
      const championship = NCAA_68.rounds[6];
      expect(championship.name).toBe("Championship");
      expect(championship.isScoring).toBe(true);
    });

    it("has exactly 3 scoring rounds (Elite Eight, Final Four, Championship)", () => {
      const scoringRounds = NCAA_68.rounds.filter((r) => r.isScoring);
      expect(scoringRounds).toHaveLength(3);
      expect(scoringRounds[0].name).toBe("Elite Eight");
      expect(scoringRounds[1].name).toBe("Final Four");
      expect(scoringRounds[2].name).toBe("Championship");
    });

    it("has exactly 4 non-scoring rounds (FF, R64, R32, S16)", () => {
      const nonScoringRounds = NCAA_68.rounds.filter((r) => !r.isScoring);
      expect(nonScoringRounds).toHaveLength(4);
    });
  });

  describe("Scoring Round Types", () => {
    it("identifies Elite Eight as quarterfinals", () => {
      const type = getScoringRoundType("Elite Eight", NCAA_68);
      expect(type).toBe("quarterfinals");
    });

    it("identifies Final Four as semifinals", () => {
      const type = getScoringRoundType("Final Four", NCAA_68);
      expect(type).toBe("semifinals");
    });

    it("identifies Championship as finals", () => {
      const type = getScoringRoundType("Championship", NCAA_68);
      expect(type).toBe("finals");
    });

    it("returns null for non-scoring rounds", () => {
      expect(getScoringRoundType("First Four", NCAA_68)).toBeNull();
      expect(getScoringRoundType("Round of 64", NCAA_68)).toBeNull();
      expect(getScoringRoundType("Round of 32", NCAA_68)).toBeNull();
      expect(getScoringRoundType("Sweet Sixteen", NCAA_68)).toBeNull();
    });
  });

  describe("Tournament Math", () => {
    it("has correct number of total matches", () => {
      // 4 + 32 + 16 + 8 + 4 + 2 + 1 = 67 total matches
      const totalMatches = NCAA_68.rounds.reduce((sum, round) => sum + round.matchCount, 0);
      expect(totalMatches).toBe(67);
    });

    it("eliminates 60 teams before Elite Eight (4+32+16+8)", () => {
      const nonScoringRounds = NCAA_68.rounds.filter((r) => !r.isScoring);
      const teamsEliminatedBeforeEliteEight = nonScoringRounds.reduce(
        (sum, round) => sum + round.matchCount,
        0
      );
      expect(teamsEliminatedBeforeEliteEight).toBe(60); // 4+32+16+8
    });

    it("has 8 teams in Elite Eight (top 8 scoring)", () => {
      const eliteEightRound = NCAA_68.rounds.find((r) => r.name === "Elite Eight");
      const teamsInEliteEight = (eliteEightRound?.matchCount ?? 0) * 2;
      expect(teamsInEliteEight).toBe(8);
    });

    it("verifies only 8 teams score fantasy points (Q18)", () => {
      // Elite Eight has 4 matches = 8 teams
      // These are the ONLY teams that can score fantasy points
      const scoringRounds = NCAA_68.rounds.filter((r) => r.isScoring);
      const eliteEightMatches = scoringRounds[0].matchCount;
      const scoringTeamsCount = eliteEightMatches * 2;
      expect(scoringTeamsCount).toBe(8);
    });
  });

  describe("Phase 2.8 Requirements", () => {
    it("meets requirement: 68 total teams", () => {
      expect(NCAA_68.totalTeams).toBe(68);
    });

    it("meets requirement: 4 First Four play-in games", () => {
      const firstFour = NCAA_68.rounds[0];
      expect(firstFour.matchCount).toBe(4);
    });

    it("meets requirement: Only Elite Eight and beyond score points", () => {
      const scoringRoundNames = NCAA_68.rounds
        .filter((r) => r.isScoring)
        .map((r) => r.name);
      expect(scoringRoundNames).toEqual(["Elite Eight", "Final Four", "Championship"]);
    });

    it("meets requirement: Early elimination = 0 points (per Q20)", () => {
      const firstFour = NCAA_68.rounds[0];
      const roundOf64 = NCAA_68.rounds[1];
      const roundOf32 = NCAA_68.rounds[2];
      const sweetSixteen = NCAA_68.rounds[3];

      expect(firstFour.isScoring).toBe(false); // 0 points
      expect(roundOf64.isScoring).toBe(false); // 0 points
      expect(roundOf32.isScoring).toBe(false); // 0 points
      expect(sweetSixteen.isScoring).toBe(false); // 0 points
    });

    it("verifies total participants: 60 direct + 8 First Four", () => {
      const firstFourTeams = 4 * 2; // 4 games × 2 teams = 8 teams
      const directSeeds = 68 - firstFourTeams; // 60 teams directly seeded
      expect(directSeeds).toBe(60);
      expect(firstFourTeams).toBe(8);
      expect(directSeeds + firstFourTeams).toBe(68);
    });
  });

  describe("Region Config (2026)", () => {
    it("has 4 named regions", () => {
      expect(NCAA_68.regions).toBeDefined();
      expect(NCAA_68.regions).toHaveLength(4);
    });

    it("has regions in order: East, South, West, Midwest", () => {
      const names = (NCAA_68.regions ?? []).map((r) => r.name);
      expect(names).toEqual(["East", "South", "West", "Midwest"]);
    });

    it("East has 16 direct seeds and no play-ins", () => {
      const east = (NCAA_68.regions ?? [])[0];
      expect(east.directSeeds).toHaveLength(16);
      expect(east.playIns).toHaveLength(0);
    });

    it("South has 15 direct seeds and one 16-seed play-in", () => {
      const south = (NCAA_68.regions ?? [])[1];
      expect(south.directSeeds).toHaveLength(15);
      expect(south.playIns).toHaveLength(1);
      expect(south.playIns[0].seedSlot).toBe(16);
    });

    it("West has 15 direct seeds and one 11-seed play-in", () => {
      const west = (NCAA_68.regions ?? [])[2];
      expect(west.directSeeds).toHaveLength(15);
      expect(west.playIns).toHaveLength(1);
      expect(west.playIns[0].seedSlot).toBe(11);
    });

    it("Midwest has 14 direct seeds and two play-ins (11 and 16)", () => {
      const midwest = (NCAA_68.regions ?? [])[3];
      expect(midwest.directSeeds).toHaveLength(14);
      expect(midwest.playIns).toHaveLength(2);
      expect(midwest.playIns[0].seedSlot).toBe(11);
      expect(midwest.playIns[1].seedSlot).toBe(16);
    });

    it("total direct seeds across all regions equals 60", () => {
      const total = (NCAA_68.regions ?? []).reduce((sum, r) => sum + r.directSeeds.length, 0);
      expect(total).toBe(60);
    });

    it("total play-in teams across all regions equals 8", () => {
      const total = (NCAA_68.regions ?? []).reduce(
        (sum, r) => sum + r.playIns.reduce((s, pi) => s + pi.teams, 0),
        0
      );
      expect(total).toBe(8);
    });

    it("direct + play-in teams sum to 68", () => {
      const direct = (NCAA_68.regions ?? []).reduce((sum, r) => sum + r.directSeeds.length, 0);
      const playIn = (NCAA_68.regions ?? []).reduce(
        (sum, r) => sum + r.playIns.reduce((s, pi) => s + pi.teams, 0),
        0
      );
      expect(direct + playIn).toBe(68);
    });
  });

  describe("buildNCAA68SlotMap", () => {
    const slotMap = buildNCAA68SlotMap(NCAA_68.regions ?? []);

    it("East starts at index 0", () => {
      expect(slotMap.directOffsets[0]).toBe(0);
    });

    it("South starts at index 16 (after East's 16 direct seeds)", () => {
      expect(slotMap.directOffsets[1]).toBe(16);
    });

    it("West starts at index 31 (after East 16 + South 15)", () => {
      expect(slotMap.directOffsets[2]).toBe(31);
    });

    it("Midwest starts at index 46 (after East 16 + South 15 + West 15)", () => {
      expect(slotMap.directOffsets[3]).toBe(46);
    });

    it("totalDirect is 60", () => {
      expect(slotMap.totalDirect).toBe(60);
    });

    it("totalPlayIn is 8", () => {
      expect(slotMap.totalPlayIn).toBe(8);
    });

    it("has 4 play-in offsets", () => {
      expect(slotMap.playInOffsets).toHaveLength(4);
    });

    it("FF1 (South 16-seed) starts at index 60", () => {
      const ff1 = slotMap.playInOffsets[0];
      expect(ff1.regionIndex).toBe(1); // South
      expect(ff1.seedSlot).toBe(16);
      expect(ff1.startIndex).toBe(60);
    });

    it("FF2 (West 11-seed) starts at index 62", () => {
      const ff2 = slotMap.playInOffsets[1];
      expect(ff2.regionIndex).toBe(2); // West
      expect(ff2.seedSlot).toBe(11);
      expect(ff2.startIndex).toBe(62);
    });

    it("FF3 (Midwest 11-seed) starts at index 64", () => {
      const ff3 = slotMap.playInOffsets[2];
      expect(ff3.regionIndex).toBe(3); // Midwest
      expect(ff3.seedSlot).toBe(11);
      expect(ff3.startIndex).toBe(64);
    });

    it("FF4 (Midwest 16-seed) starts at index 66", () => {
      const ff4 = slotMap.playInOffsets[3];
      expect(ff4.regionIndex).toBe(3); // Midwest
      expect(ff4.seedSlot).toBe(16);
      expect(ff4.startIndex).toBe(66);
    });
  });

  describe("matchIndexForSeedSlot", () => {
    it("seed 16 is at match index 0 (1v16)", () => {
      expect(matchIndexForSeedSlot(16)).toBe(0);
    });

    it("seed 1 is at match index 0 (1v16)", () => {
      expect(matchIndexForSeedSlot(1)).toBe(0);
    });

    it("seed 11 is at match index 4 (6v11)", () => {
      expect(matchIndexForSeedSlot(11)).toBe(4);
    });

    it("seed 6 is at match index 4 (6v11)", () => {
      expect(matchIndexForSeedSlot(6)).toBe(4);
    });

    it("all 8 standard seeding pairs are covered", () => {
      for (const [hi, lo] of STANDARD_BRACKET_SEEDING) {
        expect(() => matchIndexForSeedSlot(hi)).not.toThrow();
        expect(() => matchIndexForSeedSlot(lo)).not.toThrow();
      }
    });
  });

  describe("First Four → Round of 64 match number derivation", () => {
    const slotMap = buildNCAA68SlotMap(NCAA_68.regions ?? []);

    // r64MatchNumber = regionIndex * 8 + matchIndexForSeedSlot(seedSlot) + 1
    it("FF1 (South region index 1, seed 16) → R64 match 9", () => {
      const ff1 = slotMap.playInOffsets[0]; // South 16-seed
      const r64 = ff1.regionIndex * 8 + matchIndexForSeedSlot(ff1.seedSlot) + 1;
      expect(r64).toBe(9); // East occupies matches 1-8; South match index 0 → 9
    });

    it("FF2 (West region index 2, seed 11) → R64 match 21", () => {
      const ff2 = slotMap.playInOffsets[1]; // West 11-seed
      const r64 = ff2.regionIndex * 8 + matchIndexForSeedSlot(ff2.seedSlot) + 1;
      expect(r64).toBe(21); // West starts at 17; match index 4 → 17+4=21
    });

    it("FF3 (Midwest region index 3, seed 11) → R64 match 29", () => {
      const ff3 = slotMap.playInOffsets[2]; // Midwest 11-seed
      const r64 = ff3.regionIndex * 8 + matchIndexForSeedSlot(ff3.seedSlot) + 1;
      expect(r64).toBe(29); // Midwest starts at 25; match index 4 → 25+4=29
    });

    it("FF4 (Midwest region index 3, seed 16) → R64 match 25", () => {
      const ff4 = slotMap.playInOffsets[3]; // Midwest 16-seed
      const r64 = ff4.regionIndex * 8 + matchIndexForSeedSlot(ff4.seedSlot) + 1;
      expect(r64).toBe(25); // Midwest starts at 25; match index 0 → 25
    });
  });
});
