/**
 * Invoice Details API
 *
 * GET /api/billing/invoice/[invoiceId]
 *
 * Fetches invoice details from Stripe with authorization checks.
 *
 * Data Flow:
 * 1. Authenticate user (NextAuth or OTP token)
 * 2. Parse query parameters (format type)
 * 3. Fetch user and extract customer IDs
 * 4. Retrieve invoice from Stripe
 * 5. Verify user has access to the invoice
 * 6. Return response in requested format
 *
 * Query Parameters:
 * @param format - Response format: 'json' | 'pdf' | 'redirect' (default: 'json')
 *
 * Response Formats:
 * - json: Returns detailed invoice object
 * - pdf: Redirects to Stripe PDF download URL
 * - redirect: Redirects to hosted invoice page
 *
 * Error Codes:
 * - 401: Unauthorized (no valid authentication)
 * - 403: Access denied (invoice belongs to different customer)
 * - 404: User not found / No billing account / Invoice not found
 * - 500: Server error
 */

import { NextRequest, NextResponse } from "next/server";
import { logger } from "@/lib/logger";
import { auth } from "@/lib/auth";
import { getAuthenticatedConvexUser } from "@/lib/convex-auth-state";
import {
  retrieveStripeInvoice,
  transformInvoiceToDetails,
  userCanAccessInvoice,
  type InvoiceResponseFormat,
  type CustomerIdSet,
} from "@/lib/billing";
import { convexQuery } from "@/lib/convex-client";

// ============================================================================
// TYPES
// ============================================================================

interface RouteParams {
  params: Promise<{ invoiceId: string }>;
}

interface QueryParams {
  format: InvoiceResponseFormat;
}

// ============================================================================
// MAIN HANDLER
// ============================================================================

export async function GET(request: NextRequest, { params }: RouteParams) {
  try {
    const { invoiceId } = await params;

    // Step 1: Authenticate
    const user = await getAuthenticatedConvexUser(request, auth);
    if (!user) {
      return unauthorizedResponse();
    }

    // Step 2: Parse query params
    const queryParams = parseQueryParams(request.url);

    const activeSubscription = await convexQuery<Record<string, unknown>>(
      "billing/queries:getActiveSubscription",
      { userId: user.id },
    );

    const customerIds: CustomerIdSet = new Set<string>();
    if (user.stripeCustomerId) customerIds.add(user.stripeCustomerId);
    if (activeSubscription?.stripeCustomerId) {
      customerIds.add(activeSubscription.stripeCustomerId as string);
    }

    if (customerIds.size === 0) {
      return noBillingAccountResponse();
    }

    // Step 4: Retrieve invoice from Stripe
    const invoice = await retrieveStripeInvoice(invoiceId);
    if (!invoice) {
      return invoiceNotFoundResponse();
    }

    // Step 5: Verify access
    if (!userCanAccessInvoice(invoice, customerIds)) {
      return accessDeniedResponse(
        user.id,
        invoice.customer as string,
        customerIds,
      );
    }

    // Step 6: Return response in requested format
    return buildResponse(invoice, queryParams.format, user.id);
  } catch (error) {
    return handleError(error);
  }
}

// ============================================================================
// AUTHENTICATION
// ============================================================================

function unauthorizedResponse(): NextResponse {
  return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

// ============================================================================
// QUERY PARAMETER PARSING
// ============================================================================

const VALID_FORMATS: InvoiceResponseFormat[] = ["json", "pdf", "redirect"];

function parseQueryParams(url: string): QueryParams {
  const { searchParams } = new URL(url);
  const rawFormat = searchParams.get("format") || "json";

  const format: InvoiceResponseFormat = VALID_FORMATS.includes(
    rawFormat as InvoiceResponseFormat,
  )
    ? (rawFormat as InvoiceResponseFormat)
    : "json";

  return { format };
}

// ============================================================================
// ERROR RESPONSES
// ============================================================================

function noBillingAccountResponse(): NextResponse {
  return NextResponse.json(
    { error: "No billing account found" },
    { status: 404 },
  );
}

function invoiceNotFoundResponse(): NextResponse {
  return NextResponse.json({ error: "Invoice not found" }, { status: 404 });
}

function accessDeniedResponse(
  userId: string,
  invoiceCustomer: string,
  customerIds: CustomerIdSet,
): NextResponse {
  logger.warn("Invoice access denied - customer mismatch", {
    userId,
    invoiceCustomer,
    userCustomers: Array.from(customerIds),
  });

  return NextResponse.json({ error: "Access denied" }, { status: 403 });
}

// ============================================================================
// RESPONSE BUILDING
// ============================================================================

function buildResponse(
  invoice: Awaited<ReturnType<typeof retrieveStripeInvoice>>,
  format: InvoiceResponseFormat,
  userId: string,
): NextResponse {
  if (!invoice) {
    return invoiceNotFoundResponse();
  }

  // Handle PDF format
  if (format === "pdf") {
    if (!invoice.invoice_pdf) {
      return NextResponse.json(
        { error: "PDF not available for this invoice" },
        { status: 404 },
      );
    }
    return NextResponse.redirect(invoice.invoice_pdf);
  }

  // Handle redirect format
  if (format === "redirect") {
    if (!invoice.hosted_invoice_url) {
      return NextResponse.json(
        { error: "Hosted invoice page not available" },
        { status: 404 },
      );
    }
    return NextResponse.redirect(invoice.hosted_invoice_url);
  }

  // Handle JSON format (default)
  const invoiceDetails = transformInvoiceToDetails(invoice);

  logger.info("Invoice retrieved", {
    userId,
    invoiceId: invoice.id,
  });

  return NextResponse.json({ invoice: invoiceDetails });
}

// ============================================================================
// ERROR HANDLING
// ============================================================================

function handleError(error: unknown): NextResponse {
  logger.error("Invoice route error", {
    error: error instanceof Error ? error.message : "Unknown error",
    stack: error instanceof Error ? error.stack : undefined,
  });

  return NextResponse.json(
    { error: "Failed to retrieve invoice" },
    { status: 500 },
  );
}
