/**
 * Tests for Billing Service
 */

jest.mock("@/lib/supabase");
jest.mock("@/lib/db");
jest.mock("@/lib/convex-client");

jest.mock("@/lib/stripe", () => ({
  stripe: {
    subscriptions: {
      retrieve: jest.fn(),
      update: jest.fn(),
    },
    invoices: {
      createPreview: jest.fn(),
    },
  },
}));

import {
  AVAILABLE_PLANS,
  calculateProration,
  getAvailablePlans,
} from "@/lib/services/billing-service";

describe("Billing Service", () => {
  describe("AVAILABLE_PLANS", () => {
    it("should have starter, professional, and enterprise plans", () => {
      expect(AVAILABLE_PLANS).toHaveProperty("starter");
      expect(AVAILABLE_PLANS).toHaveProperty("professional");
      expect(AVAILABLE_PLANS).toHaveProperty("enterprise");
    });

    it("each plan should have required fields", () => {
      for (const [, plan] of Object.entries(AVAILABLE_PLANS)) {
        expect(plan).toHaveProperty("name");
        expect(plan).toHaveProperty("monthlyPrice");
        expect(plan).toHaveProperty("features");
        expect(Array.isArray(plan.features)).toBe(true);
      }
    });

    it("plans should have increasing prices", () => {
      expect(AVAILABLE_PLANS.starter.monthlyPrice).toBeLessThan(
        AVAILABLE_PLANS.professional.monthlyPrice,
      );
      expect(AVAILABLE_PLANS.professional.monthlyPrice).toBeLessThan(
        AVAILABLE_PLANS.enterprise.monthlyPrice,
      );
    });
  });

  describe("calculateProration", () => {
    it("should calculate upgrade proration correctly", () => {
      const currentSub = {
        planType: "starter" as const,
        status: "active" as const,
        amount: AVAILABLE_PLANS.starter.monthlyPrice,
        daysRemaining: 15,
        daysInPeriod: 30,
        currentPeriodEnd: new Date(),
        stripeSubscriptionId: "sub_test",
      };

      const result = calculateProration(
        currentSub,
        AVAILABLE_PLANS.professional,
      );

      expect(result).toHaveProperty("chargeForNewPlan");
      expect(result).toHaveProperty("creditFromCurrentPlan");
      expect(result).toHaveProperty("immediateCharge");
      expect(result.isUpgrade).toBe(true);
      expect(result.immediateCharge).toBeGreaterThan(0);
    });

    it("should return negative immediate charge for downgrades", () => {
      const currentSub = {
        planType: "enterprise" as const,
        status: "active" as const,
        amount: AVAILABLE_PLANS.enterprise.monthlyPrice,
        daysRemaining: 15,
        daysInPeriod: 30,
        currentPeriodEnd: new Date(),
        stripeSubscriptionId: "sub_test",
      };

      const result = calculateProration(currentSub, AVAILABLE_PLANS.starter);
      expect(result.isUpgrade).toBe(false);
      expect(result.immediateCharge).toBeLessThanOrEqual(0);
    });

    it("should handle zero days remaining", () => {
      const currentSub = {
        planType: "starter" as const,
        status: "active" as const,
        amount: AVAILABLE_PLANS.starter.monthlyPrice,
        daysRemaining: 0,
        daysInPeriod: 30,
        currentPeriodEnd: new Date(),
        stripeSubscriptionId: "sub_test",
      };

      const result = calculateProration(
        currentSub,
        AVAILABLE_PLANS.professional,
      );
      expect(result.creditFromCurrentPlan).toBe(0);
      expect(result.chargeForNewPlan).toBe(0);
    });
  });

  describe("getAvailablePlans", () => {
    it("should return upgrade and downgrade options for starter", async () => {
      const result = await getAvailablePlans("starter");
      expect(result).toHaveProperty("upgrades");
      expect(result).toHaveProperty("downgrades");
      expect(result).toHaveProperty("current");
      expect(result.upgrades.length).toBeGreaterThan(0);
      expect(result.downgrades.length).toBe(0);
    });

    it("should return downgrade options for enterprise", async () => {
      const result = await getAvailablePlans("enterprise");
      expect(result.upgrades.length).toBe(0);
      expect(result.downgrades.length).toBeGreaterThan(0);
    });

    it("should return both upgrade and downgrade for professional", async () => {
      const result = await getAvailablePlans("professional");
      expect(result.upgrades.length).toBeGreaterThan(0);
      expect(result.downgrades.length).toBeGreaterThan(0);
    });

    it("should remove setup fee for existing customers", async () => {
      const result = await getAvailablePlans("starter", true);
      result.upgrades.forEach((plan) => {
        expect(plan.setupFee).toBeUndefined();
      });
    });
  });
});
