/**
 * Unit tests for pricing helper functions
 * Tests proration calculations for upgrades and downgrades
 */

import {
  calculateProratedUpgrade,
  calculateDowngradeCredit,
  shouldChargeSetupFee,
  getSetupFeeAmount,
  convertPrice,
  getDaysUntilBilling,
  getCurrencyFromCountry,
} from "@/lib/utils/pricing-helpers";
import type { PricingTier } from "@/lib/types/pricing";

describe("Pricing Helper Functions", () => {
  describe("calculateProratedUpgrade", () => {
    it("should calculate correct prorated charge for monthly upgrade", () => {
      // Scenario: User upgrades from 966 to 1200 with 14 days remaining
      const result = calculateProratedUpgrade(
        966, // current price
        1200, // new price
        14, // days remaining
        "monthly",
      );

      // Expected calculation:
      // Current daily: 966/30 = 32.20
      // New daily: 1200/30 = 40.00
      // Unused credit: 32.20 * 14 = 450.80
      // New plan cost: 40.00 * 14 = 560.00
      // Prorated charge: 560 - 450.80 = 109.20
      expect(result).toBeCloseTo(109.2, 1);
    });

    it("should handle upgrade with full month remaining", () => {
      const result = calculateProratedUpgrade(500, 1000, 30, "monthly");

      // Full month: should charge difference for whole month
      // 1000 - 500 = 500
      expect(result).toBeCloseTo(500, 1);
    });

    it("should handle upgrade with 1 day remaining", () => {
      const result = calculateProratedUpgrade(966, 1200, 1, "monthly");

      // Only 1 day: small prorated charge
      // (1200/30 * 1) - (966/30 * 1) = 40 - 32.20 = 7.80
      expect(result).toBeCloseTo(7.8, 1);
    });

    it("should handle yearly billing period", () => {
      const result = calculateProratedUpgrade(
        11592, // 966 * 12
        14400, // 1200 * 12
        180, // half year
        "yearly",
      );

      expect(result).toBeGreaterThan(0);
      expect(result).toBeLessThan(14400); // Less than full year price
    });

    it("should never return negative values", () => {
      const result = calculateProratedUpgrade(
        1000,
        800, // Downgrade scenario (wrong function used)
        15,
        "monthly",
      );

      expect(result).toBe(0); // Should return 0, not negative
    });
  });

  describe("calculateDowngradeCredit", () => {
    it("should calculate correct credit for monthly downgrade", () => {
      // Scenario: User downgrades from 966 to 500 with 14 days remaining
      const result = calculateDowngradeCredit(
        966, // current price
        500, // new price
        14, // days remaining
        "monthly",
      );

      // Expected calculation:
      // Current daily: 966/30 = 32.20
      // New daily: 500/30 = 16.67
      // Unused credit: 32.20 * 14 = 450.80
      // New plan cost: 16.67 * 14 = 233.38
      // Excess credit: 450.80 - 233.38 = 217.42
      // Next bill: 500 - 217.42 = 282.58
      expect(result.creditAmount).toBeCloseTo(217.42, 0); // Relaxed precision
      expect(result.nextBillingCharge).toBeCloseTo(282.58, 0);
      expect(result.creditApplied).toBeCloseTo(217.42, 0);
    });

    it("should handle large credit that covers entire next month", () => {
      const result = calculateDowngradeCredit(
        1000,
        200,
        29, // Almost full month
        "monthly",
      );

      // Credit should be substantial
      expect(result.creditAmount).toBeGreaterThan(700);
      // Next billing should be very low or zero
      expect(result.nextBillingCharge).toBeLessThan(200);
    });

    it("should handle downgrade with 1 day remaining", () => {
      const result = calculateDowngradeCredit(966, 500, 1, "monthly");

      // Only 1 day: small credit
      expect(result.creditAmount).toBeLessThan(50);
      // With only 1 day credit (15.53), next bill is 500 - 15.53 = 484.47
      expect(result.nextBillingCharge).toBeCloseTo(484, 0);
    });

    it("should never create negative charges", () => {
      const result = calculateDowngradeCredit(1000, 200, 29, "monthly");

      expect(result.nextBillingCharge).toBeGreaterThanOrEqual(0);
    });

    it("should handle yearly billing period", () => {
      const result = calculateDowngradeCredit(
        11592, // 966 * 12
        6000, // 500 * 12
        180, // half year
        "yearly",
      );

      expect(result.creditAmount).toBeGreaterThan(0);
      expect(result.nextBillingCharge).toBeLessThanOrEqual(6000);
    });
  });

  describe("shouldChargeSetupFee", () => {
    const tierWithFee: PricingTier = {
      id: "tier2",
      nameEn: "Pro",
      nameAr: "محترف",
      priceGBP: 299,
      setupGBP: 199, // Has setup fee
      popular: true,
      featuresEn: ["Feature 1"],
      featuresAr: ["ميزة 1"],
    };

    const tierNoFee: PricingTier = {
      id: "tier1",
      nameEn: "Starter",
      nameAr: "مبتدئ",
      priceGBP: 179,
      setupGBP: 0, // No setup fee
      popular: false,
      featuresEn: ["Feature 1"],
      featuresAr: ["ميزة 1"],
    };

    it("should not charge setup fee if already paid", () => {
      const result = shouldChargeSetupFee(true, tierWithFee);
      expect(result).toBe(false);
    });

    it("should charge setup fee if not paid and tier has fee", () => {
      const result = shouldChargeSetupFee(false, tierWithFee);
      expect(result).toBe(true);
    });

    it("should not charge setup fee if tier has no fee", () => {
      const result = shouldChargeSetupFee(false, tierNoFee);
      expect(result).toBe(false);
    });

    it("should not charge setup fee if already paid, even if tier has fee", () => {
      const result = shouldChargeSetupFee(true, tierWithFee);
      expect(result).toBe(false);
    });
  });

  describe("getSetupFeeAmount", () => {
    const tier: PricingTier = {
      id: "tier2",
      nameEn: "Pro",
      nameAr: "محترف",
      priceGBP: 299,
      setupGBP: 199,
      popular: true,
      featuresEn: ["Feature 1"],
      featuresAr: ["ميزة 1"],
    };

    it("should convert setup fee to target currency", () => {
      const result = getSetupFeeAmount(tier, "SAR");
      // 199 GBP * 4.76 SAR rate = ~947 SAR
      expect(result).toBeCloseTo(947, -2);
    });

    it("should return 0 for tier with no setup fee", () => {
      const tierNoFee = { ...tier, setupGBP: 0 };
      const result = getSetupFeeAmount(tierNoFee, "GBP");
      expect(result).toBe(0);
    });

    it("should handle GBP to GBP conversion", () => {
      const result = getSetupFeeAmount(tier, "GBP");
      expect(result).toBe(199);
    });
  });

  describe("convertPrice", () => {
    it("should convert GBP to SAR correctly", () => {
      const result = convertPrice(100, "SAR");
      expect(result).toBe(476); // 100 * 4.76
    });

    it("should convert GBP to USD correctly", () => {
      const result = convertPrice(100, "USD");
      expect(result).toBe(127); // 100 * 1.27
    });

    it("should return same value for GBP to GBP", () => {
      const result = convertPrice(100, "GBP");
      expect(result).toBe(100);
    });

    it("should handle decimal values and round correctly", () => {
      const result = convertPrice(99.99, "AED");
      expect(result).toBeCloseTo(466, 0);
    });
  });

  describe("getDaysUntilBilling", () => {
    it("should calculate correct days for future date", () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 14);

      const result = getDaysUntilBilling(futureDate);
      expect(result).toBeGreaterThanOrEqual(13);
      expect(result).toBeLessThanOrEqual(14);
    });

    it("should return 0 for past date", () => {
      const pastDate = new Date();
      pastDate.setDate(pastDate.getDate() - 5);

      const result = getDaysUntilBilling(pastDate);
      expect(result).toBe(0);
    });

    it("should return 0 for today", () => {
      const today = new Date();
      const result = getDaysUntilBilling(today);
      expect(result).toBe(0);
    });

    it("should handle 30 days correctly", () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 30);

      const result = getDaysUntilBilling(futureDate);
      expect(result).toBeGreaterThanOrEqual(29);
      expect(result).toBeLessThanOrEqual(30);
    });
  });

  describe("getCurrencyFromCountry", () => {
    it("should return QAR for Qatar", () => {
      expect(getCurrencyFromCountry("Qatar")).toBe("QAR");
      expect(getCurrencyFromCountry("qatar")).toBe("QAR");
      expect(getCurrencyFromCountry("qa")).toBe("QAR");
    });

    it("should return SAR for Saudi Arabia", () => {
      expect(getCurrencyFromCountry("Saudi Arabia")).toBe("SAR");
      expect(getCurrencyFromCountry("sa")).toBe("SAR");
    });

    it("should return AED for UAE", () => {
      expect(getCurrencyFromCountry("UAE")).toBe("AED");
      expect(getCurrencyFromCountry("United Arab Emirates")).toBe("AED");
      expect(getCurrencyFromCountry("ae")).toBe("AED");
    });

    it("should return default QAR for unknown country", () => {
      expect(getCurrencyFromCountry("Unknown")).toBe("QAR");
      expect(getCurrencyFromCountry(undefined)).toBe("QAR");
      expect(getCurrencyFromCountry("")).toBe("QAR");
    });

    it("should handle case insensitive matching", () => {
      expect(getCurrencyFromCountry("KUWAIT")).toBe("KWD");
      expect(getCurrencyFromCountry("BaHrAiN")).toBe("BHD");
    });
  });

  describe("Integration: Upgrade + Downgrade Flow", () => {
    it("should handle upgrade then downgrade maintaining balance", () => {
      // User starts on 966/month plan
      const originalPrice = 966;
      const upgradePrice = 1200;
      const downgradePrice = 500;
      const daysRemaining = 14;

      // Step 1: Calculate upgrade
      const upgradeCharge = calculateProratedUpgrade(
        originalPrice,
        upgradePrice,
        daysRemaining,
        "monthly",
      );

      // Step 2: Immediately downgrade before period ends
      const downgradeCredit = calculateDowngradeCredit(
        upgradePrice,
        downgradePrice,
        daysRemaining,
        "monthly",
      );

      // Verify calculations are consistent
      expect(upgradeCharge).toBeGreaterThan(0);
      expect(downgradeCredit.creditAmount).toBeGreaterThan(0);
      expect(downgradeCredit.nextBillingCharge).toBeLessThan(downgradePrice);
    });

    it("should correctly handle mid-cycle plan changes", () => {
      const currentPrice = 299;
      const newPrice = 449;
      const daysInMonth = 30;

      // Test various points in billing cycle
      const earlyUpgrade = calculateProratedUpgrade(
        currentPrice,
        newPrice,
        28,
        "monthly",
      );
      const midUpgrade = calculateProratedUpgrade(
        currentPrice,
        newPrice,
        15,
        "monthly",
      );
      const lateUpgrade = calculateProratedUpgrade(
        currentPrice,
        newPrice,
        2,
        "monthly",
      );

      // Early upgrade should cost more than late upgrade
      expect(earlyUpgrade).toBeGreaterThan(midUpgrade);
      expect(midUpgrade).toBeGreaterThan(lateUpgrade);
      expect(lateUpgrade).toBeGreaterThan(0);
    });
  });

  describe("Edge Cases", () => {
    it("should handle zero price plans", () => {
      const result = calculateProratedUpgrade(0, 100, 15, "monthly");
      expect(result).toBeGreaterThan(0);
    });

    it("should handle same price plans (no change)", () => {
      const result = calculateProratedUpgrade(500, 500, 15, "monthly");
      expect(result).toBe(0);
    });

    it("should handle very small differences", () => {
      const result = calculateProratedUpgrade(100, 101, 15, "monthly");
      expect(result).toBeGreaterThan(0);
      expect(result).toBeLessThan(1);
    });

    it("should handle 0 days remaining", () => {
      const upgradeResult = calculateProratedUpgrade(500, 1000, 0, "monthly");
      const downgradeResult = calculateDowngradeCredit(1000, 500, 0, "monthly");

      expect(upgradeResult).toBe(0);
      expect(downgradeResult.creditAmount).toBe(0);
      expect(downgradeResult.nextBillingCharge).toBe(500);
    });
  });

  describe("Real-world Scenarios", () => {
    it("should calculate GCC pricing correctly", () => {
      // Scenario: Qatar clinic upgrading from Starter to Professional
      const starterGBP = 179;
      const professionalGBP = 209;

      // Convert to QAR
      const starterQAR = convertPrice(starterGBP, "QAR");
      const professionalQAR = convertPrice(professionalGBP, "QAR");

      // 15 days remaining in month
      const proratedCharge = calculateProratedUpgrade(
        starterQAR,
        professionalQAR,
        15,
        "monthly",
      );

      expect(starterQAR).toBeCloseTo(827, 0); // 179 * 4.62
      expect(professionalQAR).toBeCloseTo(966, 0); // 209 * 4.62
      expect(proratedCharge).toBeGreaterThan(0);
      expect(proratedCharge).toBeLessThan(professionalQAR);
    });

    it("should calculate UAE downgrade correctly", () => {
      // Scenario: Dubai business downgrading from Enterprise to Business
      const enterpriseGBP = 349;
      const businessGBP = 249;

      // Convert to AED
      const enterpriseAED = convertPrice(enterpriseGBP, "AED");
      const businessAED = convertPrice(businessGBP, "AED");

      // 20 days remaining
      const creditInfo = calculateDowngradeCredit(
        enterpriseAED,
        businessAED,
        20,
        "monthly",
      );

      expect(creditInfo.creditAmount).toBeGreaterThan(0);
      expect(creditInfo.nextBillingCharge).toBeGreaterThanOrEqual(0);
      expect(creditInfo.nextBillingCharge).toBeLessThanOrEqual(businessAED);
    });
  });

  describe("Setup Fee Logic", () => {
    const tierWithHighFee: PricingTier = {
      id: "tier5",
      nameEn: "Ultimate",
      nameAr: "المتكاملة",
      priceGBP: 449,
      setupGBP: 299, // Higher setup fee
      popular: false,
      featuresEn: [],
      featuresAr: [],
    };

    const tierWithStandardFee: PricingTier = {
      id: "tier2",
      nameEn: "Pro",
      nameAr: "محترف",
      priceGBP: 209,
      setupGBP: 199, // Standard setup fee
      popular: true,
      featuresEn: [],
      featuresAr: [],
    };

    it("should never double-charge setup fee", () => {
      // User already paid setup fee on tier1
      const shouldCharge1 = shouldChargeSetupFee(true, tierWithStandardFee);
      const shouldCharge2 = shouldChargeSetupFee(true, tierWithHighFee);

      expect(shouldCharge1).toBe(false);
      expect(shouldCharge2).toBe(false);
    });

    it("should charge setup fee for first-time user on tier with fee", () => {
      const result = shouldChargeSetupFee(false, tierWithStandardFee);
      expect(result).toBe(true);
    });

    it("should calculate correct setup fee amounts in different currencies", () => {
      const gbpAmount = getSetupFeeAmount(tierWithHighFee, "GBP");
      const sarAmount = getSetupFeeAmount(tierWithHighFee, "SAR");
      const aedAmount = getSetupFeeAmount(tierWithHighFee, "AED");

      expect(gbpAmount).toBe(299);
      expect(sarAmount).toBeCloseTo(1423, 0); // 299 * 4.76
      expect(aedAmount).toBeCloseTo(1393, 0); // 299 * 4.66
    });
  });

  describe("Proration Math Validation", () => {
    /**
     * Validates the exact scenario from the implementation plan
     */
    it("should match documentation example exactly", () => {
      // From SUBSCRIPTION_UPGRADE_DOWNGRADE_PLAN.md
      // Current: 966/month, New: 1200/month, 14 days remaining
      const result = calculateProratedUpgrade(966, 1200, 14, "monthly");

      // Documentation states: 109.20
      expect(result).toBeCloseTo(109.2, 1);
    });

    it("should match downgrade example from documentation", () => {
      // From SUBSCRIPTION_UPGRADE_DOWNGRADE_PLAN.md
      // Current: 966/month, New: 500/month, 14 days remaining
      const result = calculateDowngradeCredit(966, 500, 14, "monthly");

      // Documentation states:
      // Credit: 217.42
      // Next bill: 282.58
      expect(result.creditAmount).toBeCloseTo(217.42, 0); // Relaxed precision for floating point
      expect(result.nextBillingCharge).toBeCloseTo(282.58, 0);
    });
  });

  describe("Yearly Pricing Calculations", () => {
    /**
     * Tests for yearly billing calculations with 7% discount
     *
     * IMPORTANT: Base prices are in GBP, but Stripe charges in QAR
     * - GBP prices from pricing-tiers.ts: 179, 209, 249, 349, 449
     * - GBP to QAR rate: 4.62
     * - Yearly formula: (GBP × 4.62) × 12 × 0.93
     *
     * Example (tier1):
     * - £179/month → 827 QAR/month (179 × 4.62)
     * - Yearly: 827 × 12 × 0.93 = 9,229 QAR/year
     * - Monthly equivalent: 769 QAR/month (827 × 0.93)
     */

    // Import the yearly pricing functions
    const {
      YEARLY_DISCOUNT_PERCENT,
      YEARLY_DISCOUNT_MULTIPLIER,
      calculateYearlyTotal,
      calculateYearlyTotalCents,
      calculateMonthlyEquivalent,
    } = require("@/lib/utils/pricing-helpers");

    // GBP to QAR exchange rate (matching CurrencyManager)
    const GBP_TO_QAR_RATE = 4.62;

    describe("YEARLY_DISCOUNT constants", () => {
      it("should have 7% discount", () => {
        expect(YEARLY_DISCOUNT_PERCENT).toBe(7);
      });

      it("should have correct multiplier (0.93)", () => {
        expect(YEARLY_DISCOUNT_MULTIPLIER).toBeCloseTo(0.93, 2);
      });
    });

    describe("calculateYearlyTotal (GBP values for display)", () => {
      // These test the GBP display values on pricing pages
      it("should calculate tier1 GBP yearly: 179 × 12 × 0.93 = 1997.64", () => {
        const result = calculateYearlyTotal(179);
        expect(result).toBeCloseTo(1997.64, 2);
      });

      it("should calculate tier2 GBP yearly: 209 × 12 × 0.93 = 2332.44", () => {
        const result = calculateYearlyTotal(209);
        expect(result).toBeCloseTo(2332.44, 2);
      });
    });

    describe("calculateYearlyTotalCents (QAR for Stripe)", () => {
      /**
       * Real Stripe prices in QAR cents
       * Formula: Math.round(GBP × 4.62 × 100) × 12 × 0.93
       *
       * Verifies the exact calculation matches the formula
       */
      it("should calculate tier1 yearly in QAR cents correctly", () => {
        // £179 × 4.62 = 826.98 QAR → 82698 cents
        const monthlyQarCents = Math.round(179 * GBP_TO_QAR_RATE * 100);
        const result = calculateYearlyTotalCents(monthlyQarCents);
        const expected = Math.round(monthlyQarCents * 12 * 0.93);
        expect(result).toBe(expected);
        // Should be around 9,229 QAR (922,900 cents)
        expect(result / 100).toBeGreaterThan(9200);
        expect(result / 100).toBeLessThan(9300);
      });

      it("should calculate tier2 yearly in QAR cents correctly", () => {
        // £209 × 4.62 = 965.58 QAR → 96558 cents
        const monthlyQarCents = Math.round(209 * GBP_TO_QAR_RATE * 100);
        const result = calculateYearlyTotalCents(monthlyQarCents);
        const expected = Math.round(monthlyQarCents * 12 * 0.93);
        expect(result).toBe(expected);
        // Should be around 10,778 QAR
        expect(result / 100).toBeGreaterThan(10700);
        expect(result / 100).toBeLessThan(10850);
      });

      it("should calculate tier3 yearly in QAR cents correctly", () => {
        // £249 × 4.62 = 1,150.38 QAR → 115038 cents
        const monthlyQarCents = Math.round(249 * GBP_TO_QAR_RATE * 100);
        const result = calculateYearlyTotalCents(monthlyQarCents);
        const expected = Math.round(monthlyQarCents * 12 * 0.93);
        expect(result).toBe(expected);
        // Should be around 12,835 QAR
        expect(result / 100).toBeGreaterThan(12800);
        expect(result / 100).toBeLessThan(12900);
      });

      it("should calculate tier4 yearly in QAR cents correctly", () => {
        // £349 × 4.62 = 1,612.38 QAR → 161238 cents
        const monthlyQarCents = Math.round(349 * GBP_TO_QAR_RATE * 100);
        const result = calculateYearlyTotalCents(monthlyQarCents);
        const expected = Math.round(monthlyQarCents * 12 * 0.93);
        expect(result).toBe(expected);
        // Should be around 17,994 QAR
        expect(result / 100).toBeGreaterThan(17900);
        expect(result / 100).toBeLessThan(18100);
      });

      it("should calculate tier5 yearly in QAR cents correctly", () => {
        // £449 × 4.62 = 2,074.38 QAR → 207438 cents
        const monthlyQarCents = Math.round(449 * GBP_TO_QAR_RATE * 100);
        const result = calculateYearlyTotalCents(monthlyQarCents);
        const expected = Math.round(monthlyQarCents * 12 * 0.93);
        expect(result).toBe(expected);
        // Should be around 23,150 QAR
        expect(result / 100).toBeGreaterThan(23100);
        expect(result / 100).toBeLessThan(23250);
      });
    });

    describe("calculateMonthlyEquivalent", () => {
      it("should calculate discounted monthly (GBP): 179 × 0.93 = 166.47", () => {
        const result = calculateMonthlyEquivalent(179);
        expect(result).toBeCloseTo(166.47, 2);
      });

      it("should calculate discounted monthly (QAR): 827 × 0.93 = 769.11", () => {
        const monthlyQar = Math.round(179 * GBP_TO_QAR_RATE);
        const result = calculateMonthlyEquivalent(monthlyQar);
        expect(result).toBeCloseTo(769, 0);
      });
    });

    describe("Yearly pricing integration", () => {
      it("should verify yearly total equals 12 × monthly equivalent", () => {
        const monthlyQar = Math.round(209 * GBP_TO_QAR_RATE);
        const yearlyTotal = calculateYearlyTotal(monthlyQar);
        const monthlyEquivalent = calculateMonthlyEquivalent(monthlyQar);

        // Yearly total should equal 12 × monthly equivalent
        expect(yearlyTotal).toBeCloseTo(monthlyEquivalent * 12, 2);
      });

      it("should verify savings calculation is exactly 7%", () => {
        const monthlyQar = Math.round(349 * GBP_TO_QAR_RATE);
        const regularYearlyTotal = monthlyQar * 12;
        const discountedYearlyTotal = calculateYearlyTotal(monthlyQar);
        const savings = regularYearlyTotal - discountedYearlyTotal;
        const savingsPercent = (savings / regularYearlyTotal) * 100;

        expect(savingsPercent).toBeCloseTo(7, 1);
      });

      it("should handle all tier QAR prices correctly", () => {
        const tierPricesGBP = [179, 209, 249, 349, 449];

        tierPricesGBP.forEach((gbpPrice) => {
          const qarPrice = Math.round(gbpPrice * GBP_TO_QAR_RATE);
          const yearlyTotal = calculateYearlyTotal(qarPrice);
          const expected = qarPrice * 12 * 0.93;

          expect(yearlyTotal).toBeCloseTo(expected, 2);
        });
      });
    });
  });
});
