/**
 * Billing Invoice API Tests
 * Tests for /api/billing/invoice/[invoiceId] endpoint
 */

import { NextRequest } from "next/server";
import { GET } from "@/app/api/billing/invoice/[invoiceId]/route";

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

jest.mock("@/lib/stripe", () => ({
  stripe: {
    invoices: {
      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 use getAuthenticatedEmail with Convex lookup.
describe.skip("GET /api/billing/invoice/[invoiceId]", () => {
  const mockInvoice = {
    id: "in_test_123",
    number: "INV-2024-001",
    status: "paid",
    amount_paid: 11500,
    amount_due: 0,
    total: 11500,
    currency: "qar",
    hosted_invoice_url: "https://invoice.stripe.com/test",
    invoice_pdf: "https://invoice.stripe.com/test.pdf",
    created: Math.floor(new Date("2024-01-15").getTime() / 1000),
    period_start: Math.floor(new Date("2024-01-15").getTime() / 1000),
    period_end: Math.floor(new Date("2024-02-15").getTime() / 1000),
    billing_reason: "subscription_create",
    customer_email: "user@test.com",
    customer: "cus_123",
    lines: {
      data: [
        {
          description: "Business Plan - Monthly",
          amount: 11500,
          quantity: 1,
          period: {
            start: Math.floor(new Date("2024-01-15").getTime() / 1000),
            end: Math.floor(new Date("2024-02-15").getTime() / 1000),
          },
          price: {
            product: {
              name: "Business Plan",
            },
          },
        },
      ],
    },
  };

  const createRequest = (invoiceId: string, format?: string) => {
    const url = format
      ? `http://localhost:9000/api/billing/invoice/${invoiceId}?format=${format}`
      : `http://localhost:9000/api/billing/invoice/${invoiceId}`;
    return new NextRequest(url);
  };

  const createParams = (invoiceId: string) => ({
    params: Promise.resolve({ invoiceId }),
  });

  beforeEach(() => {
    jest.clearAllMocks();
  });

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

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      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 = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

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

  describe("Authorization", () => {
    it("should return 404 if user has no billing account", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: null,
        subscriptions: [],
      });

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(404);
      const data = await response.json();
      expect(data.error).toBe("No billing account found");
    });

    it("should return 403 if invoice belongs to different customer", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_different",
        subscriptions: [],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        customer: "cus_other_user", // Different customer
      });

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(403);
      const data = await response.json();
      expect(data.error).toBe("Access denied");
    });

    it("should allow access when invoice customer matches user stripeCustomerId", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue(mockInvoice);

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
    });

    it("should allow access when invoice customer matches subscription stripeCustomerId", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: null,
        subscriptions: [{ stripeCustomerId: "cus_123" }],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue(mockInvoice);

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
    });
  });

  describe("Invoice retrieval", () => {
    it("should return 404 if invoice does not exist in Stripe", async () => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [],
      });
      (stripe.invoices.retrieve as jest.Mock).mockRejectedValue({
        code: "resource_missing",
      });

      const request = createRequest("in_nonexistent");
      const response = await GET(request, createParams("in_nonexistent"));

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

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

      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(500);
      const data = await response.json();
      expect(data.error).toBe("Failed to retrieve invoice");
    });
  });

  describe("Response formats", () => {
    beforeEach(() => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue(mockInvoice);
    });

    it("should return JSON by default", async () => {
      const request = createRequest("in_test_123");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.invoice).toBeDefined();
      expect(data.invoice.id).toBe("in_test_123");
    });

    it("should return JSON when format=json", async () => {
      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.invoice.id).toBe("in_test_123");
      expect(data.invoice.number).toBe("INV-2024-001");
      expect(data.invoice.status).toBe("paid");
      expect(data.invoice.amountPaid).toBe(11500);
      expect(data.invoice.currency).toBe("qar");
      expect(data.invoice.hostedUrl).toBe("https://invoice.stripe.com/test");
      expect(data.invoice.pdfUrl).toBe("https://invoice.stripe.com/test.pdf");
    });

    it("should redirect to PDF when format=pdf", async () => {
      const request = createRequest("in_test_123", "pdf");
      const response = await GET(request, createParams("in_test_123"));

      // NextResponse.redirect returns 307 or 308 status
      expect([307, 308]).toContain(response.status);
    });

    it("should redirect to hosted invoice when format=redirect", async () => {
      const request = createRequest("in_test_123", "redirect");
      const response = await GET(request, createParams("in_test_123"));

      // NextResponse.redirect returns 307 or 308 status
      expect([307, 308]).toContain(response.status);
    });

    it("should return 404 if PDF URL not available", async () => {
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        invoice_pdf: null,
      });

      const request = createRequest("in_test_123", "pdf");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(404);
      const data = await response.json();
      expect(data.error).toBe("PDF not available for this invoice");
    });

    it("should return 404 if hosted URL not available", async () => {
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        hosted_invoice_url: null,
      });

      const request = createRequest("in_test_123", "redirect");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(404);
      const data = await response.json();
      expect(data.error).toBe("Hosted invoice page not available");
    });
  });

  describe("JSON response structure", () => {
    beforeEach(() => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [],
      });
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue(mockInvoice);
    });

    it("should include all invoice fields", async () => {
      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      const data = await response.json();
      const { invoice } = data;

      expect(invoice).toMatchObject({
        id: "in_test_123",
        number: "INV-2024-001",
        status: "paid",
        amountPaid: 11500,
        amountDue: 0,
        total: 11500,
        currency: "qar",
        hostedUrl: "https://invoice.stripe.com/test",
        pdfUrl: "https://invoice.stripe.com/test.pdf",
        billingReason: "subscription_create",
        customerEmail: "user@test.com",
      });
    });

    it("should include line items", async () => {
      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      const data = await response.json();
      const { invoice } = data;

      expect(invoice.lineItems).toHaveLength(1);
      expect(invoice.lineItems[0]).toMatchObject({
        description: "Business Plan - Monthly",
        amount: 11500,
        quantity: 1,
      });
      expect(invoice.lineItems[0].period).toBeDefined();
    });

    it("should convert timestamps to ISO strings", async () => {
      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      const data = await response.json();
      const { invoice } = data;

      expect(invoice.created).toContain("2024-01-15");
      expect(invoice.periodStart).toContain("2024-01-15");
      expect(invoice.periodEnd).toContain("2024-02-15");
    });
  });

  describe("Edge cases", () => {
    beforeEach(() => {
      (getAuthenticatedEmail as jest.Mock).mockResolvedValue("user@test.com");
      (prisma.users.findUnique as jest.Mock).mockResolvedValue({
        id: "user_123",
        stripeCustomerId: "cus_123",
        subscriptions: [],
      });
    });

    it("should handle invoice with no line items", async () => {
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        lines: { data: [] },
      });

      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

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

    it("should handle invoice with null period dates", async () => {
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        period_start: null,
        period_end: null,
      });

      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.invoice.periodStart).toBeNull();
      expect(data.invoice.periodEnd).toBeNull();
    });

    it("should handle line item without description", async () => {
      (stripe.invoices.retrieve as jest.Mock).mockResolvedValue({
        ...mockInvoice,
        lines: {
          data: [
            {
              description: null,
              amount: 11500,
              quantity: 1,
              period: null,
              price: { product: { name: "Subscription" } },
            },
          ],
        },
      });

      const request = createRequest("in_test_123", "json");
      const response = await GET(request, createParams("in_test_123"));

      expect(response.status).toBe(200);
      const data = await response.json();
      expect(data.invoice.lineItems[0].description).toBe("Subscription");
    });
  });
});
