import { NextResponse } from 'next/server';

import { withAuth, withPermission } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { errorResponse } from '@/lib/errors';
import { streamExpensePdf } from '@/lib/services/expense-pdf.service';
import { exportExpensePdfSchema } from '@/lib/validators/expenses';

/**
 * POST /api/v1/expenses/export/pdf
 *
 * Streams the expense report PDF directly to the client - body bytes
 * leave the process as pdfkit writes them, so the route is safe for
 * hundreds of expenses with full-resolution receipt images. See
 * `expense-pdf.service.ts` for the memory-budget design.
 *
 * Request body shape (zod-validated):
 *   {
 *     expenseIds?: string[]    // explicit selection (preferred)
 *     filter?: {...}           // listExpenses-style filter when no ids
 *     options: {
 *       documentName, subheader?, groupBy, includeReceipts,
 *       includeReceiptContents, includeSummary, includeDetails,
 *       includeProcessingFee, targetCurrency, pageFormat,
 *     }
 *   }
 *
 * Response: `application/pdf` binary stream + Content-Disposition.
 */
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

export const POST = withAuth(
  withPermission('expenses', 'export', async (req, ctx) => {
    try {
      const input = await parseBody(req, exportExpensePdfSchema);

      const { stream, suggestedFilename } = await streamExpensePdf({
        portId: ctx.portId,
        expenseIds: input.expenseIds,
        filter: input.filter
          ? {
              dateFrom: input.filter.dateFrom ?? null,
              dateTo: input.filter.dateTo ?? null,
              category: input.filter.category ?? null,
              paymentStatus: input.filter.paymentStatus ?? null,
              payer: input.filter.payer ?? null,
              tripLabel: input.filter.tripLabel ?? null,
              includeArchived: input.filter.includeArchived ?? false,
            }
          : undefined,
        options: input.options,
        // Forward the request abort signal so the streaming PDF builder
        // stops fetching/resizing receipts the moment the client disconnects
        // (otherwise an aborted 1000-receipt export keeps the worker busy
        // for minutes after the user navigated away - see audit finding 2).
        signal: req.signal,
      });

      // Content-Disposition filename hardening: the validator caps length
      // but `\s` matches CR/LF, which would let an attacker forge response
      // headers. Strip everything that isn't word/space/dot/dash, AND set
      // the RFC 5987 `filename*` so a UTF-8 body still survives.
      const safeFilename = suggestedFilename.replace(/[^\w. \-]+/g, '_');
      const disposition = `attachment; filename="${safeFilename}"; filename*=UTF-8''${encodeURIComponent(suggestedFilename)}`;

      return new NextResponse(stream, {
        status: 200,
        headers: {
          'Content-Type': 'application/pdf',
          'Content-Disposition': disposition,
          'Cache-Control': 'private, no-store, max-age=0',
          'X-Content-Type-Options': 'nosniff',
        },
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
