/**
 * Route handlers for `/api/v1/berths/[id]/pdf-versions` (Phase 6b).
 *
 * Lives in handlers.ts (not route.ts) so integration tests can call them
 * directly, bypassing the auth/permission middleware (per CLAUDE.md
 * "Route handler exports" convention).
 */

import { NextResponse } from 'next/server';
import { z } from 'zod';

import { type RouteHandler } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { errorResponse, ValidationError } from '@/lib/errors';
import { listBerthPdfVersions, uploadBerthPdf } from '@/lib/services/berth-pdf.service';

const postBodySchema = z.object({
  storageKey: z.string().min(1),
  fileName: z.string().min(1).max(255),
  fileSizeBytes: z.number().int().positive(),
  sha256: z.string().min(1),
  parseResults: z
    .object({
      engine: z.enum(['acroform', 'ocr', 'ai']),
      extracted: z.record(z.string(), z.unknown()).optional(),
      meanConfidence: z.number().optional(),
      warnings: z.array(z.string()).optional(),
    })
    .optional(),
});

export const getHandler: RouteHandler = async (_req, ctx, params) => {
  try {
    const versions = await listBerthPdfVersions(params.id!, ctx.portId);
    return NextResponse.json({ data: versions });
  } catch (error) {
    return errorResponse(error);
  }
};

// Defense against the post-upload register endpoint trusting an arbitrary
// storageKey from the body. The companion presign endpoint always issues
// `berths/<berthId>/uploads/<uuid>_<sanitized>` (see ./pdf-upload-url),
// and pdf-upload-url tenant-scopes the berth lookup. Without this regex,
// a rep with berths.edit could ship the storage key of a foreign-port
// PDF (signed EOI, brochure blob, another port's berth) and have the
// service repoint THIS berth's currentPdfVersionId at it - subsequent
// pdf-download serves those bytes under the rep's own permission gate.
const STORAGE_KEY_RE =
  /^berths\/[A-Za-z0-9_-]+\/uploads\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}_/;

export const postHandler: RouteHandler = async (req, ctx, params) => {
  try {
    const body = await parseBody(req, postBodySchema);
    const expectedPrefix = `berths/${params.id!}/uploads/`;
    if (!body.storageKey.startsWith(expectedPrefix) || !STORAGE_KEY_RE.test(body.storageKey)) {
      throw new ValidationError(
        'storageKey must come from the matching presign endpoint for this berth',
      );
    }
    const result = await uploadBerthPdf({
      berthId: params.id!,
      portId: ctx.portId,
      storageKey: body.storageKey,
      fileName: body.fileName,
      fileSizeBytes: body.fileSizeBytes,
      sha256: body.sha256,
      uploadedBy: ctx.userId,
      parseResult: body.parseResults
        ? {
            engine: body.parseResults.engine,
            // Reconstruct just enough of the ParseResult shape to round-trip
            // through serialization; the rep already saw the conflicts in the
            // diff dialog, so storing the engine + extracted is what we need
            // for audit.
            fields: Object.fromEntries(
              Object.entries(body.parseResults.extracted ?? {}).map(([k, v]) => {
                if (v && typeof v === 'object' && 'value' in v) {
                  const obj = v as { value: unknown; confidence?: number };
                  return [
                    k,
                    {
                      value: obj.value as never,
                      confidence: typeof obj.confidence === 'number' ? obj.confidence : 1,
                      engine: body.parseResults!.engine,
                    },
                  ];
                }
                return [k, undefined];
              }),
            ) as never,
            meanConfidence: body.parseResults.meanConfidence ?? 1,
            warnings: body.parseResults.warnings ?? [],
          }
        : undefined,
    });
    return NextResponse.json({ data: result }, { status: 201 });
  } catch (error) {
    return errorResponse(error);
  }
};
