import { describe, it, expect } from "vitest";
import { SIMPLE_16, SIMPLE_32, NCAA_68 } from "~/lib/bracket-templates";
import { calculateFantasyPoints, calculateAveragedPoints, type ScoringRules } from "../scoring-rules";

const DEFAULT_SCORING: ScoringRules = {
  pointsFor1st: 100,
  pointsFor2nd: 70,
  pointsFor3rd: 50,
  pointsFor4th: 40,
  pointsFor5th: 25,
  pointsFor6th: 25,
  pointsFor7th: 15,
  pointsFor8th: 15,
};

describe("Bracket Integration Tests - Phase 2.7", () => {
  describe("16-Team Bracket", () => {
    it("has correct template structure", () => {
      expect(SIMPLE_16.totalTeams).toBe(16);
      expect(SIMPLE_16.rounds).toHaveLength(4);
      expect(SIMPLE_16.scoringStartsAtRound).toBe("Quarterfinals");

      // Round of 16 doesn't score
      const round1 = SIMPLE_16.rounds[0];
      expect(round1.name).toBe("Round of 16");
      expect(round1.isScoring).toBe(false);
      expect(round1.matchCount).toBe(8);

      // Quarterfinals and beyond score
      const round2 = SIMPLE_16.rounds[1];
      expect(round2.name).toBe("Quarterfinals");
      expect(round2.isScoring).toBe(true);
      expect(round2.matchCount).toBe(4);

      const round3 = SIMPLE_16.rounds[2];
      expect(round3.name).toBe("Semifinals");
      expect(round3.isScoring).toBe(true);
      expect(round3.matchCount).toBe(2);

      const round4 = SIMPLE_16.rounds[3];
      expect(round4.name).toBe("Finals");
      expect(round4.isScoring).toBe(true);
      expect(round4.matchCount).toBe(1);
    });

    it("calculates correct points for each placement", () => {
      // Champion (1st)
      const champion = calculateFantasyPoints(1, DEFAULT_SCORING);
      expect(champion).toBe(100);

      // Runner-up (2nd)
      const runnerUp = calculateFantasyPoints(2, DEFAULT_SCORING);
      expect(runnerUp).toBe(70);

      // Semifinals losers (3rd/4th share)
      const sfLosers = calculateAveragedPoints([3, 4], DEFAULT_SCORING);
      expect(sfLosers).toBe(45); // (50 + 40) / 2

      // Quarterfinals losers (5th-8th share)
      const qfLosers = calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING);
      expect(qfLosers).toBe(20); // (25 + 25 + 15 + 15) / 4

      // Round of 16 losers (early elimination = 0 points per Q20)
      const earlyElimination = calculateFantasyPoints(0, DEFAULT_SCORING);
      expect(earlyElimination).toBe(0);
    });

    it("simulates complete 16-team bracket scoring", () => {
      // Simulate a complete bracket
      const results = {
        // Finals
        champion: 1, // 100 points
        runnerUp: 2, // 70 points

        // Semifinals losers (2 teams share 3rd/4th)
        sfLoser1: 3, // 45 points (avg of 3rd and 4th)
        sfLoser2: 3, // 45 points

        // Quarterfinals losers (4 teams share 5th-8th)
        qfLoser1: 5, // 20 points (avg of 5-8)
        qfLoser2: 5, // 20 points
        qfLoser3: 5, // 20 points
        qfLoser4: 5, // 20 points

        // Round of 16 losers (8 teams get 0 points)
        r16Loser1: 0, // 0 points
        r16Loser2: 0, // 0 points
        r16Loser3: 0, // 0 points
        r16Loser4: 0, // 0 points
        r16Loser5: 0, // 0 points
        r16Loser6: 0, // 0 points
        r16Loser7: 0, // 0 points
        r16Loser8: 0, // 0 points
      };

      // Verify champion
      expect(calculateFantasyPoints(results.champion, DEFAULT_SCORING)).toBe(100);

      // Verify runner-up
      expect(calculateFantasyPoints(results.runnerUp, DEFAULT_SCORING)).toBe(70);

      // Verify SF losers
      expect(calculateFantasyPoints(results.sfLoser1, DEFAULT_SCORING)).toBe(50); // Gets 3rd place points
      expect(calculateFantasyPoints(results.sfLoser2, DEFAULT_SCORING)).toBe(50);
      // When averaged: (50 + 40) / 2 = 45
      expect(calculateAveragedPoints([3, 4], DEFAULT_SCORING)).toBe(45);

      // Verify QF losers
      expect(calculateFantasyPoints(results.qfLoser1, DEFAULT_SCORING)).toBe(25); // Gets 5th place points
      // When averaged: (25 + 25 + 15 + 15) / 4 = 20
      expect(calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING)).toBe(20);

      // Verify Round of 16 losers get 0 points
      expect(calculateFantasyPoints(results.r16Loser1, DEFAULT_SCORING)).toBe(0);
      expect(calculateFantasyPoints(results.r16Loser8, DEFAULT_SCORING)).toBe(0);

      // Total points distributed (if one team drafted all participants)
      // 100 + 70 + 45 + 45 + 20 + 20 + 20 + 20 + 0*8 = 340
      const totalPoints =
        100 + 70 + 45 + 45 + 20 + 20 + 20 + 20 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0;
      expect(totalPoints).toBe(340);
    });

    it("verifies only top 8 finishers score fantasy points", () => {
      // Winners and top 8 get points
      expect(calculateFantasyPoints(1, DEFAULT_SCORING)).toBeGreaterThan(0);
      expect(calculateFantasyPoints(8, DEFAULT_SCORING)).toBeGreaterThan(0);

      // Round of 16 losers (positions 9-16) get 0 points
      expect(calculateFantasyPoints(0, DEFAULT_SCORING)).toBe(0);
      expect(calculateFantasyPoints(9, DEFAULT_SCORING)).toBe(0);
      expect(calculateFantasyPoints(16, DEFAULT_SCORING)).toBe(0);
    });
  });

  describe("32-Team Bracket", () => {
    it("has correct template structure", () => {
      expect(SIMPLE_32.totalTeams).toBe(32);
      expect(SIMPLE_32.rounds).toHaveLength(5);
      expect(SIMPLE_32.scoringStartsAtRound).toBe("Quarterfinals");

      // Round of 32 and Round of 16 don't score
      const round1 = SIMPLE_32.rounds[0];
      expect(round1.name).toBe("Round of 32");
      expect(round1.isScoring).toBe(false);
      expect(round1.matchCount).toBe(16);

      const round2 = SIMPLE_32.rounds[1];
      expect(round2.name).toBe("Round of 16");
      expect(round2.isScoring).toBe(false);
      expect(round2.matchCount).toBe(8);

      // Quarterfinals and beyond score
      const round3 = SIMPLE_32.rounds[2];
      expect(round3.name).toBe("Quarterfinals");
      expect(round3.isScoring).toBe(true);
      expect(round3.matchCount).toBe(4);

      const round4 = SIMPLE_32.rounds[3];
      expect(round4.name).toBe("Semifinals");
      expect(round4.isScoring).toBe(true);

      const round5 = SIMPLE_32.rounds[4];
      expect(round5.name).toBe("Finals");
      expect(round5.isScoring).toBe(true);
    });

    it("calculates correct points for each placement", () => {
      // Same as 16-team for top 8
      expect(calculateFantasyPoints(1, DEFAULT_SCORING)).toBe(100);
      expect(calculateFantasyPoints(2, DEFAULT_SCORING)).toBe(70);
      expect(calculateAveragedPoints([3, 4], DEFAULT_SCORING)).toBe(45);
      expect(calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING)).toBe(20);

      // Early elimination (9th-32nd) = 0 points
      expect(calculateFantasyPoints(0, DEFAULT_SCORING)).toBe(0);
    });

    it("simulates complete 32-team bracket scoring", () => {
      // Top 8 finishers
      const topEight = {
        champion: 100,
        runnerUp: 70,
        sfLoser1: 45,
        sfLoser2: 45,
        qfLoser1: 20,
        qfLoser2: 20,
        qfLoser3: 20,
        qfLoser4: 20,
      };

      // 24 early eliminations (Round of 32 + Round of 16)
      const earlyEliminations = Array(24).fill(0);

      // Total points
      const topEightTotal = Object.values(topEight).reduce((sum, pts) => sum + pts, 0);
      expect(topEightTotal).toBe(340);

      const earlyEliminationTotal = earlyEliminations.reduce((sum, pts) => sum + pts, 0);
      expect(earlyEliminationTotal).toBe(0);

      const grandTotal = topEightTotal + earlyEliminationTotal;
      expect(grandTotal).toBe(340);
    });

    it("verifies 24 teams eliminated early get 0 points", () => {
      // Round of 32: 16 losers
      // Round of 16: 8 losers
      // Total: 24 teams get 0 points

      // Only top 8 score
      const scoringTeams = 8;
      const nonScoringTeams = 32 - scoringTeams;
      expect(nonScoringTeams).toBe(24);

      // All non-scoring teams get 0 points
      for (let i = 0; i < nonScoringTeams; i++) {
        expect(calculateFantasyPoints(0, DEFAULT_SCORING)).toBe(0);
      }
    });
  });

  describe("NCAA March Madness (68 Teams) - Elite Eight Scoring", () => {
    it("has correct template structure", () => {
      expect(NCAA_68.totalTeams).toBe(68);
      expect(NCAA_68.rounds).toHaveLength(7);
      expect(NCAA_68.scoringStartsAtRound).toBe("Elite Eight");

      // First Four through Sweet Sixteen don't score
      expect(NCAA_68.rounds[0].name).toBe("First Four");
      expect(NCAA_68.rounds[0].isScoring).toBe(false);

      expect(NCAA_68.rounds[1].name).toBe("Round of 64");
      expect(NCAA_68.rounds[1].isScoring).toBe(false);

      expect(NCAA_68.rounds[2].name).toBe("Round of 32");
      expect(NCAA_68.rounds[2].isScoring).toBe(false);

      expect(NCAA_68.rounds[3].name).toBe("Sweet Sixteen");
      expect(NCAA_68.rounds[3].isScoring).toBe(false);

      // Elite Eight and beyond score
      expect(NCAA_68.rounds[4].name).toBe("Elite Eight");
      expect(NCAA_68.rounds[4].isScoring).toBe(true);

      expect(NCAA_68.rounds[5].name).toBe("Final Four");
      expect(NCAA_68.rounds[5].isScoring).toBe(true);

      expect(NCAA_68.rounds[6].name).toBe("Championship");
      expect(NCAA_68.rounds[6].isScoring).toBe(true);
    });

    it("verifies only Elite Eight and beyond score points (per Q18)", () => {
      // Teams eliminated before Elite Eight get 0 points
      // First Four: 4 losers
      // Round of 64: 32 losers
      // Round of 32: 16 losers
      // Sweet Sixteen: 8 losers
      // Total: 60 teams get 0 points

      const nonScoringTeams = 4 + 32 + 16 + 8;
      expect(nonScoringTeams).toBe(60);

      // Only Elite Eight and beyond (8 teams) score
      const scoringTeams = 8;
      expect(scoringTeams + nonScoringTeams).toBe(68);
    });

    it("simulates complete March Madness bracket scoring", () => {
      // Top 8 finishers (Elite Eight and beyond)
      const topEight = {
        champion: 100,
        runnerUp: 70,
        finalFourLoser1: 45,
        finalFourLoser2: 45,
        eliteEightLoser1: 20,
        eliteEightLoser2: 20,
        eliteEightLoser3: 20,
        eliteEightLoser4: 20,
      };

      // 60 early eliminations
      const earlyEliminations = Array(60).fill(0);

      // Total points
      const topEightTotal = Object.values(topEight).reduce((sum, pts) => sum + pts, 0);
      expect(topEightTotal).toBe(340);

      const earlyEliminationTotal = earlyEliminations.reduce((sum, pts) => sum + pts, 0);
      expect(earlyEliminationTotal).toBe(0);

      const grandTotal = topEightTotal + earlyEliminationTotal;
      expect(grandTotal).toBe(340);
    });

    it("verifies Elite Eight losers share 5th-8th placement", () => {
      // Elite Eight has 4 matches (Quarterfinals round)
      // Losers share 5th-8th = (25 + 25 + 15 + 15) / 4 = 20 points each
      const eliteEightLosers = calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING);
      expect(eliteEightLosers).toBe(20);
    });

    it("verifies Final Four losers share 3rd-4th placement", () => {
      // Final Four has 2 matches (Semifinals round)
      // Losers share 3rd-4th = (50 + 40) / 2 = 45 points each
      const finalFourLosers = calculateAveragedPoints([3, 4], DEFAULT_SCORING);
      expect(finalFourLosers).toBe(45);
    });
  });

  describe("Cross-Template Consistency", () => {
    it("verifies all templates award same total points for top 8", () => {
      // Regardless of bracket size, top 8 always get the same points
      const expectedTotal = 100 + 70 + 45 + 45 + 20 + 20 + 20 + 20;

      // 16-team bracket
      const team16Total = 100 + 70 + 45 + 45 + 20 + 20 + 20 + 20;
      expect(team16Total).toBe(expectedTotal);

      // 32-team bracket
      const team32Total = 100 + 70 + 45 + 45 + 20 + 20 + 20 + 20;
      expect(team32Total).toBe(expectedTotal);

      // NCAA 68-team bracket
      const ncaaTotal = 100 + 70 + 45 + 45 + 20 + 20 + 20 + 20;
      expect(ncaaTotal).toBe(expectedTotal);

      // All should be 340
      expect(expectedTotal).toBe(340);
    });

    it("verifies scoring rounds are consistent across templates", () => {
      // All templates should have exactly 3 scoring rounds
      const simple16ScoringRounds = SIMPLE_16.rounds.filter((r) => r.isScoring);
      expect(simple16ScoringRounds).toHaveLength(3); // QF, SF, Finals

      const simple32ScoringRounds = SIMPLE_32.rounds.filter((r) => r.isScoring);
      expect(simple32ScoringRounds).toHaveLength(3); // QF, SF, Finals

      const ncaaScoringRounds = NCAA_68.rounds.filter((r) => r.isScoring);
      expect(ncaaScoringRounds).toHaveLength(3); // Elite Eight, Final Four, Championship
    });

    it("verifies non-scoring rounds correctly identified", () => {
      // 16-team: 1 non-scoring round
      const simple16NonScoring = SIMPLE_16.rounds.filter((r) => !r.isScoring);
      expect(simple16NonScoring).toHaveLength(1);

      // 32-team: 2 non-scoring rounds
      const simple32NonScoring = SIMPLE_32.rounds.filter((r) => !r.isScoring);
      expect(simple32NonScoring).toHaveLength(2);

      // NCAA 68: 4 non-scoring rounds
      const ncaaNonScoring = NCAA_68.rounds.filter((r) => !r.isScoring);
      expect(ncaaNonScoring).toHaveLength(4);
    });
  });

  describe("Placement 0 for Early Eliminations (Q20)", () => {
    it("placement 0 awards 0 fantasy points", () => {
      const points = calculateFantasyPoints(0, DEFAULT_SCORING);
      expect(points).toBe(0);
    });

    it("placement 0 works with custom scoring rules", () => {
      const customScoring: ScoringRules = {
        pointsFor1st: 500,
        pointsFor2nd: 400,
        pointsFor3rd: 300,
        pointsFor4th: 200,
        pointsFor5th: 100,
        pointsFor6th: 100,
        pointsFor7th: 50,
        pointsFor8th: 50,
      };

      const points = calculateFantasyPoints(0, customScoring);
      expect(points).toBe(0);
    });

    it("early eliminations do not affect averaged points", () => {
      // If we incorrectly averaged in 0-point placements, we'd get wrong results
      // Correct: avg(5,6,7,8) = (25+25+15+15)/4 = 20
      // Wrong: avg(0,5,6,7,8) = (0+25+25+15+15)/5 = 16

      const correct = calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING);
      expect(correct).toBe(20);

      // Should NOT include 0 in averaging
      const wrong = calculateAveragedPoints([0, 5, 6, 7, 8], DEFAULT_SCORING);
      expect(wrong).not.toBe(20);
      expect(wrong).toBe(16); // This would be incorrect scoring
    });
  });
});
