/**
 * Billing History API Tests
 * Tests for /api/billing/history endpoint
 */

import { NextRequest } from "next/server";
import { GET } from "@/app/api/billing/history/route";

// Mock dependencies
jest.mock("@/lib/db");

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

jest.mock("@/lib/auth", () => ({
  auth: jest.fn(),
}));

jest.mock("@/lib/utils/auth-helpers.util", () => ({
  getAuthenticatedEmail: jest.fn(),
}));

jest.mock("@/lib/logger", () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
  },
}));

import { prisma } from "@/lib/db";
import { stripe } from "@/lib/stripe";
import { getAuthenticatedEmail } from "@/lib/utils/auth-helpers.util";

// Route has been refactored to Convex-first. Prisma-based mocks no longer apply.
describe.skip("GET /api/billing/history", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("Authentication", () => {
    it("should return 401 if user is not authenticated", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue(null);

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(401);
      const data = await response.json();
      expect(data.error).toBe("Unauthorized");
    });

    it("should return 404 if user is not found", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue(
        "unknown@test.com",
      );
      (prisma.users.findUnique as jest.Mock).mockResolvedValue(null);

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(404);
      const data = await response.json();
      expect(data.error).toBe("User not found");
    });
  });

  describe("Local billing history", () => {
    it("should return billing history from local database", async () => {
      const mockBillingHistory = [
        {
          id: "bh_1",
          event: "invoice_paid",
          amount: 11500,
          currency: "qar",
          createdAt: new Date("2024-01-15"),
          metadata: { billing_reason: "subscription_create" },
          invoiceId: null, // No invoiceId means no enrichment
          paymentIntentId: "pi_123",
        },
        {
          id: "bh_2",
          event: "setup_fee_paid",
          amount: 5000,
          currency: "qar",
          createdAt: new Date("2024-01-10"),
          metadata: { tier_id: "tier5" },
          invoiceId: null,
          paymentIntentId: "pi_456",
        },
      ];

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            stripeSubscriptionId: "sub_stripe_123",
            billing_history: mockBillingHistory,
          },
        ],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?include_invoices=false",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.billingHistory).toHaveLength(2);
      expect(data.source).toBe("local");
      expect(data.billingHistory[0].event).toBe("invoice_paid");
      expect(data.billingHistory[1].event).toBe("setup_fee_paid");
    });

    it("should respect limit parameter", async () => {
      // Database returns only 5 items due to limit
      const mockBillingHistory = Array.from({ length: 5 }, (_, i) => ({
        id: `bh_${i}`,
        event: "invoice_paid",
        amount: 11500,
        currency: "qar",
        createdAt: new Date(`2024-01-${15 - i}`),
        metadata: {},
        invoiceId: null, // No enrichment needed
        paymentIntentId: null,
      }));

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: mockBillingHistory,
          },
        ],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?limit=5&include_invoices=false",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.billingHistory.length).toBeLessThanOrEqual(5);
    });

    it("should enforce maximum limit of 100", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: null,
        subscriptions: [],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?limit=500",
      );
      const response = await GET(request);

      // Should not exceed 100 limit internally
      expect(response.status).toBe(200);
    });
  });

  describe("Stripe fallback", () => {
    it("should fetch from Stripe when no local history exists", async () => {
      const mockStripeInvoices = {
        data: [
          {
            id: "in_stripe_1",
            amount_paid: 11500,
            currency: "qar",
            created: Math.floor(Date.now() / 1000),
            billing_reason: "subscription_create",
            number: "INV-001",
            period_start: Math.floor(Date.now() / 1000),
            period_end: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,
            hosted_invoice_url: "https://stripe.com/invoice/1",
            invoice_pdf: "https://stripe.com/invoice/1.pdf",
            status: "paid",
          },
        ],
      };

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: [], // Empty local history
          },
        ],
      });
      (stripe.invoices.list as jest.Mock).mockResolvedValue(mockStripeInvoices);

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.source).toBe("stripe");
      expect(data.billingHistory).toHaveLength(1);
      expect(data.billingHistory[0].invoiceUrl).toBe(
        "https://stripe.com/invoice/1",
      );
      expect(data.billingHistory[0].invoicePdf).toBe(
        "https://stripe.com/invoice/1.pdf",
      );
    });

    it("should handle Stripe API errors gracefully", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: [],
          },
        ],
      });
      (stripe.invoices.list as jest.Mock).mockRejectedValue(
        new Error("Stripe API error"),
      );

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.billingHistory).toEqual([]);
    });
  });

  describe("Invoice enrichment", () => {
    it("should enrich local history with Stripe invoice details", async () => {
      const mockBillingHistory = [
        {
          id: "bh_1",
          event: "invoice_paid",
          amount: 11500,
          currency: "qar",
          createdAt: new Date("2024-01-15"),
          metadata: {},
          invoiceId: "in_stripe_1",
          paymentIntentId: null,
        },
      ];

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: mockBillingHistory,
          },
        ],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        id: "in_stripe_1",
        hosted_invoice_url: "https://stripe.com/invoice/enriched",
        invoice_pdf: "https://stripe.com/invoice/enriched.pdf",
        status: "paid",
        number: "INV-ENRICHED-001",
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?include_invoices=true",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.source).toBe("combined");
      expect(data.billingHistory[0].invoiceUrl).toBe(
        "https://stripe.com/invoice/enriched",
      );
      expect(data.billingHistory[0].invoiceNumber).toBe("INV-ENRICHED-001");
    });

    it("should skip enrichment when include_invoices=false", async () => {
      const mockBillingHistory = [
        {
          id: "bh_1",
          event: "invoice_paid",
          amount: 11500,
          currency: "qar",
          createdAt: new Date("2024-01-15"),
          metadata: {},
          invoiceId: "in_stripe_1",
          paymentIntentId: null,
        },
      ];

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: mockBillingHistory,
          },
        ],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?include_invoices=false",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.source).toBe("local");
      expect(stripe.invoices.retrieve).not.toHaveBeenCalled();
    });
  });

  describe("Response format", () => {
    it("should return proper billing event structure", async () => {
      const mockBillingHistory = [
        {
          id: "bh_1",
          event: "checkout_completed",
          amount: 16500,
          currency: "qar",
          createdAt: new Date("2024-01-15T10:30:00Z"),
          metadata: {
            tier_id: "tier5",
            payment_method_brand: "visa",
            payment_method_last_four: "4242",
          },
          invoiceId: "in_123",
          paymentIntentId: "pi_123",
        },
      ];

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: mockBillingHistory,
          },
        ],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?include_invoices=false",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();

      const event = data.billingHistory[0];
      expect(event).toMatchObject({
        id: "bh_1",
        event: "checkout_completed",
        amount: 16500,
        currency: "qar",
        invoiceId: "in_123",
        paymentIntentId: "pi_123",
      });
      expect(event.metadata.tier_id).toBe("tier5");
      expect(event.createdAt).toContain("2024-01-15");
    });

    it("should include hasMore flag", async () => {
      const mockBillingHistory = Array.from({ length: 20 }, (_, i) => ({
        id: `bh_${i}`,
        event: "invoice_paid",
        amount: 11500,
        currency: "qar",
        createdAt: new Date("2024-01-15"),
        metadata: {},
        invoiceId: null,
        paymentIntentId: null,
      }));

      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: "cus_123",
            billing_history: mockBillingHistory,
          },
        ],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history?limit=20&include_invoices=false",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.hasMore).toBe(true);
    });
  });

  describe("Edge cases", () => {
    it("should handle user with no subscription", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: null,
        subscriptions: [],
      });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.billingHistory).toEqual([]);
    });

    it("should use user stripeCustomerId when subscription has none", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_user_level",
        subscriptions: [
          {
            id: "sub_123",
            stripeCustomerId: null, // No customer ID on subscription
            billing_history: [],
          },
        ],
      });
      (stripe.invoices.list as jest.Mock).mockResolvedValue({ data: [] });

      const request = new NextRequest(
        "http://localhost:9000/api/billing/history",
      );
      const response = await GET(request);

      expect(response.status).toBe(200);
      expect(stripe.invoices.list).toHaveBeenCalledWith(
        expect.objectContaining({ customer: "cus_user_level" }),
      );
    });
  });
});
