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

import { withAuth, withPermission } from '@/lib/api/helpers';
import { errorResponse, ValidationError } from '@/lib/errors';
import {
  uploadDocumentForSigning,
  type CustomDocumentType,
  type CustomRecipientRole,
} from '@/lib/services/custom-document-upload.service';
import { isPdfMagic } from '@/lib/services/berth-pdf-parser';

/**
 * Generic upload-for-signing endpoint — used by the Documents Hub and
 * entity doc-tab "Send file for signature" buttons where the doc isn't
 * tied to an interest's sales pipeline. The interest-scoped sibling
 * (/api/v1/interests/[id]/upload-for-signing) is still the path for
 * EOI / Contract / Reservation flows so the pipeline side effects fire.
 *
 * documentType is locked to 'generic' here. Optional `entity` +
 * `folderId` route the file to the right place on the Documents Hub.
 *
 * Permission: documents.send_for_signing — same gate as the
 * interest-scoped flow.
 */

const recipientSchema = z.object({
  name: z.string().min(1).max(200),
  email: z.string().email(),
  role: z.enum(['SIGNER', 'APPROVER', 'CC']),
  signingOrder: z.number().int().positive(),
});

const fieldSchema = z.object({
  recipientIndex: z.number().int().nonnegative(),
  type: z.enum([
    'SIGNATURE',
    'FREE_SIGNATURE',
    'INITIALS',
    'DATE',
    'EMAIL',
    'NAME',
    'TEXT',
    'NUMBER',
    'CHECKBOX',
    'DROPDOWN',
    'RADIO',
  ]),
  pageNumber: z.number().int().positive(),
  pageX: z.number().min(0).max(100),
  pageY: z.number().min(0).max(100),
  pageWidth: z.number().positive().max(100),
  pageHeight: z.number().positive().max(100),
  fieldMeta: z.record(z.string(), z.unknown()).optional(),
});

const entitySchema = z.object({
  type: z.enum(['client', 'company', 'yacht']),
  id: z.string().min(1),
});

const MAX_PDF_BYTES = 50 * 1024 * 1024;

function parseJsonField<T>(raw: unknown, schema: z.ZodType<T>, label: string): T {
  if (typeof raw !== 'string') {
    throw new ValidationError(`Missing or non-string '${label}' field`);
  }
  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch {
    throw new ValidationError(`'${label}' is not valid JSON`);
  }
  const result = schema.safeParse(parsed);
  if (!result.success) {
    throw new ValidationError(`'${label}' validation failed: ${result.error.issues[0]?.message}`);
  }
  return result.data;
}

export const POST = withAuth(
  withPermission('documents', 'send_for_signing', async (req, ctx) => {
    try {
      const form = await req.formData();

      // ─── file ──────────────────────────────────────────────────
      const file = form.get('file');
      if (!file || !(file instanceof File)) {
        throw new ValidationError('Missing file');
      }
      if (file.size > MAX_PDF_BYTES) {
        throw new ValidationError(`File exceeds ${MAX_PDF_BYTES / 1024 / 1024} MB cap`);
      }
      const buffer = Buffer.from(await file.arrayBuffer());
      if (!isPdfMagic(buffer)) {
        throw new ValidationError('Uploaded file is not a PDF');
      }

      // ─── scalar fields ─────────────────────────────────────────
      const title = z.string().min(1).max(255).parse(form.get('title'));
      const invitationMessageRaw = form.get('invitationMessage');
      const invitationMessage =
        typeof invitationMessageRaw === 'string'
          ? z.string().max(1000).parse(invitationMessageRaw)
          : null;

      // Optional entity / folder routing.
      const entityRaw = form.get('entity');
      const entity =
        typeof entityRaw === 'string' && entityRaw.length > 0
          ? parseJsonField(entityRaw, entitySchema, 'entity')
          : null;
      const folderIdRaw = form.get('folderId');
      const folderId =
        typeof folderIdRaw === 'string' && folderIdRaw.length > 0 ? folderIdRaw : null;

      // ─── JSON fields ───────────────────────────────────────────
      const recipients = parseJsonField(
        form.get('recipients'),
        z.array(recipientSchema).min(1).max(20),
        'recipients',
      );
      const fields = parseJsonField(
        form.get('fields'),
        z.array(fieldSchema).min(1).max(200),
        'fields',
      );

      const result = await uploadDocumentForSigning({
        interestId: null,
        entity,
        folderId,
        portId: ctx.portId,
        portSlug: ctx.portSlug,
        documentType: 'generic' satisfies CustomDocumentType,
        title,
        pdfBuffer: buffer,
        filename: file.name || 'document.pdf',
        recipients: recipients.map((r) => ({
          name: r.name,
          email: r.email,
          role: r.role as CustomRecipientRole,
          signingOrder: r.signingOrder,
        })),
        fields,
        invitationMessage,
        meta: {
          userId: ctx.userId,
          portId: ctx.portId,
          ipAddress: ctx.ipAddress,
          userAgent: ctx.userAgent,
        },
      });

      return NextResponse.json({ data: result }, { status: 201 });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
