import { NextResponse } from 'next/server';
import { and, eq } from 'drizzle-orm';
import { PDFDocument } from 'pdf-lib';

import { withAuth, withPermission } from '@/lib/api/helpers';
import { db } from '@/lib/db';
import { documentTemplates, files } from '@/lib/db/schema/documents';
import { errorResponse, NotFoundError, ValidationError } from '@/lib/errors';
import { buildStoragePath } from '@/lib/minio';
import { getStorageBackend } from '@/lib/storage';
import { env } from '@/lib/env';
import { createAuditLog } from '@/lib/audit';

const MAX_PDF_BYTES = 10 * 1024 * 1024;
const PDF_MAGIC = Buffer.from([0x25, 0x50, 0x44, 0x46, 0x2d]); // "%PDF-"

/**
 * Phase 7.2 - replace the template's source PDF while preserving the
 * field map. The existing `overlay_positions` is kept exactly as-is;
 * the client warns when the new page count truncates the previous set
 * (markers on now-orphaned pages are invisible at render time).
 *
 * Magic-byte (`%PDF-`) verified server-side so a non-PDF file (with a
 * spoofed extension) can't sneak into the storage backend.
 */
export const POST = withAuth(
  withPermission('admin', 'manage_settings', async (req, ctx, params) => {
    try {
      const template = await db.query.documentTemplates.findFirst({
        where: and(eq(documentTemplates.id, params.id!), eq(documentTemplates.portId, ctx.portId)),
      });
      if (!template) throw new NotFoundError('Template');

      const form = await req.formData();
      const file = form.get('file');
      if (!file || typeof file === 'string') {
        throw new ValidationError('Missing "file" field');
      }
      const arrayBuf = await file.arrayBuffer();
      if (arrayBuf.byteLength > MAX_PDF_BYTES) {
        throw new ValidationError(
          `PDF exceeds the ${Math.floor(MAX_PDF_BYTES / 1024 / 1024)} MB cap`,
        );
      }
      const buf = Buffer.from(arrayBuf);
      if (buf.length < PDF_MAGIC.length || !buf.subarray(0, PDF_MAGIC.length).equals(PDF_MAGIC)) {
        throw new ValidationError('Uploaded file does not look like a PDF (missing %PDF- header)');
      }

      // Resolve the page count so the client can surface a warning if
      // the new PDF truncates the prior page set + orphaned markers
      // exist. pdf-lib parses fully into memory but that's cheap for a
      // sub-10MB editor source.
      const pdfDoc = await PDFDocument.load(buf);
      const pageCount = pdfDoc.getPageCount();

      const fileId = crypto.randomUUID();
      const storagePath = buildStoragePath(
        ctx.portSlug,
        'document-templates',
        template.id,
        fileId,
        'pdf',
      );
      const backend = await getStorageBackend();
      await backend.put(storagePath, buf, {
        contentType: 'application/pdf',
        sizeBytes: buf.length,
      });

      const [fileRecord] = await db
        .insert(files)
        .values({
          portId: ctx.portId,
          filename: `${template.name.toLowerCase().replace(/\s+/g, '-')}.pdf`,
          originalName: file.name || `${template.name}.pdf`,
          mimeType: 'application/pdf',
          sizeBytes: String(buf.length),
          storagePath,
          storageBucket: env.MINIO_BUCKET,
          category: 'eoi',
          uploadedBy: ctx.userId,
        })
        .returning();

      await db
        .update(documentTemplates)
        .set({ sourceFileId: fileRecord!.id, updatedAt: new Date() })
        .where(eq(documentTemplates.id, template.id));

      void createAuditLog({
        userId: ctx.userId,
        portId: ctx.portId,
        action: 'update',
        entityType: 'documentTemplate',
        entityId: template.id,
        metadata: {
          action: 'replace_source_pdf',
          fileId: fileRecord!.id,
          pageCount,
          sizeBytes: buf.length,
        },
        ipAddress: ctx.ipAddress,
        userAgent: ctx.userAgent,
      });

      return NextResponse.json({
        data: { sourceFileId: fileRecord!.id, pageCount },
      });
    } catch (error) {
      return errorResponse(error);
    }
  }),
);
