import { describe, it, expect } from "vitest";
import { getGuaranteedMinimumPosition } from "../scoring-calculator";
import { calculateAveragedPoints, calculateBracketPoints, calculateFantasyPoints, 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("Progressive Floor Scoring", () => {
  describe("getGuaranteedMinimumPosition", () => {
    describe("Standard bracket rounds", () => {
      it("returns null for Finals — winner is finalized as 1st", () => {
        expect(getGuaranteedMinimumPosition("Finals", null, true)).toBeNull();
        expect(getGuaranteedMinimumPosition("Championship", null, true)).toBeNull();
        expect(getGuaranteedMinimumPosition("Super Bowl", null, true)).toBeNull();
        expect(getGuaranteedMinimumPosition("NBA Finals", null, true)).toBeNull();
        expect(getGuaranteedMinimumPosition("Grand Final", "afl_10", true)).toBeNull();
      });

      it("returns 2 for Semifinals — winners go to Final, guaranteed at worst 2nd", () => {
        expect(getGuaranteedMinimumPosition("Semifinals", null, true)).toBe(2);
        expect(getGuaranteedMinimumPosition("Final Four", null, true)).toBe(2);
        expect(getGuaranteedMinimumPosition("Conference Finals", null, true)).toBe(2);
        expect(getGuaranteedMinimumPosition("Conference Championship", null, true)).toBe(2);
      });

      it("returns 3 for Quarterfinals — winners go to Semis, guaranteed at worst 3rd-4th", () => {
        expect(getGuaranteedMinimumPosition("Quarterfinals", null, true)).toBe(3);
        expect(getGuaranteedMinimumPosition("Elite Eight", null, true)).toBe(3);
        expect(getGuaranteedMinimumPosition("Divisional", null, true)).toBe(3);
        expect(getGuaranteedMinimumPosition("Conference Semifinals", null, true)).toBe(3);
      });

      it("returns 5 for earlier scoring rounds — winners are guaranteed top-8", () => {
        expect(getGuaranteedMinimumPosition("Round of 16", null, true)).toBe(5);
        expect(getGuaranteedMinimumPosition("Round of 32", null, true)).toBe(5);
        expect(getGuaranteedMinimumPosition("Wild Card Round", null, true)).toBe(5);
      });

      it("returns null for non-scoring rounds — winning doesn't guarantee points yet", () => {
        expect(getGuaranteedMinimumPosition("Round of 16", null, false)).toBeNull();
        expect(getGuaranteedMinimumPosition("First Four", null, false)).toBeNull();
        expect(getGuaranteedMinimumPosition("Quarterfinals", null, false)).toBeNull();
      });
    });

    describe("AFL bracket (afl_10)", () => {
      it("returns 3 for Qualifying Finals winners — advance to Preliminary Finals", () => {
        expect(getGuaranteedMinimumPosition("Qualifying Finals", "afl_10", true)).toBe(3);
      });

      it("returns 5 for Elimination Finals winners — advance to Semi-Finals", () => {
        expect(getGuaranteedMinimumPosition("Elimination Finals", "afl_10", true)).toBe(5);
      });

      it("returns 3 for AFL Semi-Finals winners — advance to Preliminary Finals", () => {
        expect(getGuaranteedMinimumPosition("Semi-Finals", "afl_10", true)).toBe(3);
      });

      it("returns 2 for Preliminary Finals winners — advance to Grand Final", () => {
        expect(getGuaranteedMinimumPosition("Preliminary Finals", "afl_10", true)).toBe(2);
      });
    });
  });

  describe("Progressive point values (guaranteed minimum points)", () => {
    it("standard bracket: 8-team path to champion earns correct floor at each stage", () => {
      // Win Quarterfinals → guaranteed at worst 3rd-4th
      const afterQF = calculateAveragedPoints([3, 4], DEFAULT_SCORING);
      expect(afterQF).toBe(45); // (50 + 40) / 2

      // Win Semifinals → guaranteed at worst 2nd
      const afterSF = calculateFantasyPoints(2, DEFAULT_SCORING);
      expect(afterSF).toBe(70);

      // Win Finals → 1st place
      const champion = calculateFantasyPoints(1, DEFAULT_SCORING);
      expect(champion).toBe(100);
    });

    it("standard bracket: losing in Round of 16 earns 0 pts (non-scoring)", () => {
      // Non-scoring round losers get eliminated with 0 pts
      expect(calculateFantasyPoints(0, DEFAULT_SCORING)).toBe(0);
    });

    it("standard bracket: Quarterfinal survivors start at 20 pts floor (avg of 5-8)", () => {
      // 8 winners from R16 advance to QF. If all 8 lose QF → tied 5th-8th → 20 pts each.
      // But only 4 lose QF; the 4 winners advance. Their guaranteed minimum = avg(3, 4).
      // The R16 winners' guaranteed minimum (before QF is played) would be avg(5,6,7,8) = 20.
      const r16WinnersFloor = calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING);
      expect(r16WinnersFloor).toBe(20); // (25+25+15+15)/4
    });

    it("AFL: Elimination Finals winners get 5th-6th floor (avg)", () => {
      // EF winners advance to Semi-Finals. If they lose Semi-Finals → 5th-6th.
      const efWinnersFloor = calculateAveragedPoints([5, 6], DEFAULT_SCORING);
      expect(efWinnersFloor).toBe(25); // (25+25)/2 = 25
    });

    it("AFL: Qualifying Finals winners get 3rd-4th floor (avg)", () => {
      // QF winners advance to Preliminary Finals. If they lose → 3rd-4th.
      const qfWinnersFloor = calculateAveragedPoints([3, 4], DEFAULT_SCORING);
      expect(qfWinnersFloor).toBe(45); // (50+40)/2
    });

    it("AFL: Qualifying Finals losers (second chance) get 5th-6th floor", () => {
      // QF losers drop to Semi-Finals. If they lose Semi-Finals → 5th-6th.
      const qfLosersFloor = calculateAveragedPoints([5, 6], DEFAULT_SCORING);
      expect(qfLosersFloor).toBe(25); // (25+25)/2 = 25
    });
  });

  describe("Non-scoring round provisional floors", () => {
    it("R16 winners (non-scoring, standard bracket) get T5 floor = 20 pts", () => {
      // After winning a non-scoring R16, participants are guaranteed top-8.
      // processPlayoffEvent assigns finalPosition=5 with isPartialScore=true.
      // calculateBracketPoints maps position 5 → avg(5-8) = 20 pts (standard tier).
      expect(calculateBracketPoints(5, DEFAULT_SCORING)).toBe(20);
    });

    it("non-scoring floor (pos 5) is less than QF winner floor (pos 3)", () => {
      // Advancing past QF increases the floor from 20 → 45 pts.
      const nonScoringFloor = calculateBracketPoints(5, DEFAULT_SCORING);
      const afterQFFloor = calculateBracketPoints(3, DEFAULT_SCORING);
      expect(afterQFFloor).toBeGreaterThan(nonScoringFloor);
    });

    it("position 5 stored for non-scoring winner gives correct 20 pts via calculateBracketPoints", () => {
      // The non-scoring branch stores finalPosition=5 (fixed).
      // calculateBracketPoints correctly averages [5,6,7,8] regardless of how many
      // winners the non-scoring round produced.
      expect(calculateBracketPoints(5, DEFAULT_SCORING)).toBe(20); // (25+25+15+15)/4
    });
  });

  describe("AFL bracket tier scoring (afl_10)", () => {
    it("Elimination Finals losers (pos 7) get T7-T8 avg = 15 pts, not avg([5-8])", () => {
      // EF losers share T7-T8 with one other team; they should NOT share with T5-T6 teams.
      expect(calculateBracketPoints(7, DEFAULT_SCORING, "afl_10")).toBe(15); // avg([7,8]) = 15
    });

    it("Semi-Finals losers (pos 5) get T5-T6 avg = 25 pts, not avg([5-8])", () => {
      // SF losers share T5-T6 with one other team; they should NOT be averaged with T7-T8.
      expect(calculateBracketPoints(5, DEFAULT_SCORING, "afl_10")).toBe(25); // avg([5,6]) = 25
    });

    it("T5-T6 and T7-T8 are different in AFL (25 vs 15)", () => {
      const sfLoserPoints = calculateBracketPoints(5, DEFAULT_SCORING, "afl_10");
      const efLoserPoints = calculateBracketPoints(7, DEFAULT_SCORING, "afl_10");
      expect(sfLoserPoints).toBeGreaterThan(efLoserPoints);
    });

    it("standard bracket: position 5 still gets avg([5-8]) = 20 pts without afl_10 template", () => {
      // Standard brackets have all 4 QF losers at position 5, sharing avg([5,6,7,8])
      expect(calculateBracketPoints(5, DEFAULT_SCORING)).toBe(20);
      expect(calculateBracketPoints(5, DEFAULT_SCORING, null)).toBe(20);
      expect(calculateBracketPoints(5, DEFAULT_SCORING, "ncaa_68")).toBe(20);
    });

    it("AFL pos 8 also gets T7-T8 avg = 15 pts", () => {
      expect(calculateBracketPoints(8, DEFAULT_SCORING, "afl_10")).toBe(15);
    });

    it("AFL pos 6 also gets T5-T6 avg = 25 pts", () => {
      expect(calculateBracketPoints(6, DEFAULT_SCORING, "afl_10")).toBe(25);
    });

    it("AFL 3rd-4th and 1st-2nd are same as standard", () => {
      expect(calculateBracketPoints(3, DEFAULT_SCORING, "afl_10")).toBe(45); // avg([3,4])
      expect(calculateBracketPoints(4, DEFAULT_SCORING, "afl_10")).toBe(45);
      expect(calculateBracketPoints(2, DEFAULT_SCORING, "afl_10")).toBe(70);
      expect(calculateBracketPoints(1, DEFAULT_SCORING, "afl_10")).toBe(100);
    });
  });

  describe("Progressive scoring progression", () => {
    it("score increases as participant advances through bracket", () => {
      // A participant's guaranteed minimum only increases, never decreases.
      // After each round, verify new floor > previous floor.
      const afterR16Floor = calculateAveragedPoints([5, 6, 7, 8], DEFAULT_SCORING); // 20
      const afterQFFloor = calculateAveragedPoints([3, 4], DEFAULT_SCORING); // 45
      const afterSFFloor = calculateFantasyPoints(2, DEFAULT_SCORING); // 70
      const champion = calculateFantasyPoints(1, DEFAULT_SCORING); // 100

      expect(afterQFFloor).toBeGreaterThan(afterR16Floor);
      expect(afterSFFloor).toBeGreaterThan(afterQFFloor);
      expect(champion).toBeGreaterThan(afterSFFloor);
    });

    it("final winner's partial score (2nd) is replaced by finalized 1st place score", () => {
      // Before Finals: finalist has guaranteed minimum of 2nd place
      const finalistFloor = calculateFantasyPoints(2, DEFAULT_SCORING);
      expect(finalistFloor).toBe(70);

      // After winning Finals: finalized to 1st place
      const championScore = calculateFantasyPoints(1, DEFAULT_SCORING);
      expect(championScore).toBe(100);

      // Score increased when finalized
      expect(championScore).toBeGreaterThan(finalistFloor);
    });
  });
});
