// Must be imported before pdf-parse to register DOMMatrix and PDF.js worker globals
import "pdf-parse/worker";

/**
 * /api/brands/[brandId]/enrichment-files
 *
 * GET  — list enrichment files for a brand
 * POST — upload a document, extract text SYNCHRONOUSLY, store in DB
 *
 * Files are NOT stored on disk (ephemeral in Vercel). The extracted text
 * is stored in the database. Raw file bytes are discarded after extraction.
 */

import { NextResponse } from "next/server";
import { PDFParse } from "pdf-parse";
import path from "path";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

export const dynamic = "force-dynamic";
export const maxDuration = 60;

const MAX_FILE_BYTES = 10 * 1024 * 1024;
const MAX_FILES_PER_BRAND = 20;
const ACCEPTED_EXTENSIONS = new Set([".pdf", ".doc", ".docx", ".txt"]);

async function verifyAccess(brandId: string, userId: string) {
  const brand = await prisma.brand.findUnique({
    where: { id: brandId },
    include: { organization: { include: { memberships: true } } },
  });
  if (!brand) return null;
  if (!brand.organization.memberships.some((m) => m.userId === userId)) return null;
  return brand;
}

export async function GET(_req: Request, { params }: { params: Promise<{ brandId: string }> }) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const { brandId } = await params;
  if (!await verifyAccess(brandId, user.id)) return NextResponse.json({ error: "Access denied" }, { status: 403 });

  const files = await prisma.enrichmentFile.findMany({
    where: { brandId },
    orderBy: { uploadedAt: "desc" },
    select: { id: true, fileName: true, fileType: true, fileSize: true, processingStatus: true, processingError: true, uploadedAt: true },
  });
  return NextResponse.json({ files });
}

export async function POST(req: Request, { params }: { params: Promise<{ brandId: string }> }) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const { brandId } = await params;
  if (!await verifyAccess(brandId, user.id)) return NextResponse.json({ error: "Access denied" }, { status: 403 });

  const existing = await prisma.enrichmentFile.count({ where: { brandId } });
  if (existing >= MAX_FILES_PER_BRAND) return NextResponse.json({ error: `Maximum ${MAX_FILES_PER_BRAND} files per brand` }, { status: 400 });

  const formData = await req.formData();
  const file = formData.get("file") as File | null;
  const fileType = (formData.get("fileType") as string) || "document";
  if (!file) return NextResponse.json({ error: "No file provided" }, { status: 400 });

  const ext = path.extname(file.name).toLowerCase();
  if (!ACCEPTED_EXTENSIONS.has(ext)) return NextResponse.json({ error: `Unsupported: ${ext}. Accepted: .pdf, .doc, .docx, .txt` }, { status: 400 });
  if (file.size > MAX_FILE_BYTES) return NextResponse.json({ error: "File exceeds 10MB limit" }, { status: 400 });

  const buffer = Buffer.from(await file.arrayBuffer());

  // Extract text SYNCHRONOUSLY — no fire-and-forget, no stuck "processing" state
  const { text, error } = await extractTextFromBuffer(buffer, ext, file.name);

  const record = await prisma.enrichmentFile.create({
    data: {
      brandId,
      fileName: file.name,
      fileType,
      mimeType: file.type || "application/octet-stream",
      fileSize: file.size,
      fileUrl: "db-stored", // raw file not persisted — only extracted text matters
      extractedText: text || null,
      processingStatus: text && text.length >= 50 ? "ready" : "failed",
      processingError: error || null,
    },
  });

  return NextResponse.json({ file: record }, { status: 201 });
}

/** Extract text from a file buffer. Returns { text, error }. */
async function extractTextFromBuffer(
  buffer: Buffer, ext: string, fileName: string,
): Promise<{ text: string; error: string | null }> {
  try {
    if (ext === ".txt") {
      const text = buffer.toString("utf-8").slice(0, 50000);
      return { text, error: null };
    }

    if (ext === ".pdf") {
      try {
        const parser = new PDFParse({ data: buffer });
        const result = await parser.getText();
        const text = (result.text || "").trim().slice(0, 50000);
        if (text.length < 50) return { text: "", error: "Scanned PDF — no extractable text. Upload a text-based PDF or .docx instead." };
        return { text, error: null };
      } catch (err) {
        const msg = err instanceof Error ? err.message : "";
        if (msg.includes("encrypt")) return { text: "", error: "Encrypted PDF — cannot read. Remove password protection and re-upload." };
        return { text: "", error: "PDF extraction failed — try uploading as .docx or .txt instead." };
      }
    }

    if (ext === ".docx") {
      // DOCX is a zip containing word/document.xml
      try {
        const text = extractDocxText(buffer);
        if (text.length > 10) return { text: text.slice(0, 50000), error: null };
        return { text: "", error: "Could not extract text from DOCX. Try uploading as .txt instead." };
      } catch {
        return { text: "", error: "DOCX parsing failed. Try uploading as .txt or .pdf instead." };
      }
    }

    if (ext === ".doc") {
      // .doc (old binary format) — extract readable ASCII strings
      const raw = buffer.toString("latin1").replace(/[^\x20-\x7E\n\r\t]/g, " ").replace(/\s{3,}/g, "\n").trim();
      if (raw.length > 100) return { text: raw.slice(0, 50000), error: null };
      return { text: "", error: ".doc format has limited text extraction. Try saving as .docx or .txt." };
    }

    return { text: "", error: `Unsupported format: ${ext}` };
  } catch (err) {
    return { text: "", error: err instanceof Error ? err.message : "Text extraction failed" };
  }
}

/** Extract text from DOCX zip buffer by finding word/document.xml */
function extractDocxText(buffer: Buffer): string {
  // Find "word/document.xml" in the PKZIP central directory
  // DOCX is a ZIP file — we need to find and decompress the XML entry
  const str = buffer.toString("binary");
  const xmlMarker = "word/document.xml";
  const idx = str.indexOf(xmlMarker);
  if (idx === -1) return "";

  // Find the local file header for this entry (search backwards from central dir reference)
  // Local file header signature: 0x04034b50
  let headerIdx = -1;
  for (let i = 0; i < buffer.length - 30; i++) {
    if (buffer[i] === 0x50 && buffer[i + 1] === 0x4b && buffer[i + 2] === 0x03 && buffer[i + 3] === 0x04) {
      // Check if this header's filename matches
      const fnLen = buffer.readUInt16LE(i + 26);
      const extraLen = buffer.readUInt16LE(i + 28);
      const fn = buffer.toString("binary", i + 30, i + 30 + fnLen);
      if (fn === xmlMarker) {
        headerIdx = i;
        break;
      }
    }
  }
  if (headerIdx === -1) return "";

  const compression = buffer.readUInt16LE(headerIdx + 8);
  const compSize = buffer.readUInt32LE(headerIdx + 18);
  const fnLen = buffer.readUInt16LE(headerIdx + 26);
  const extraLen = buffer.readUInt16LE(headerIdx + 28);
  const dataStart = headerIdx + 30 + fnLen + extraLen;
  const rawData = buffer.subarray(dataStart, dataStart + compSize);

  let xml: string;
  if (compression === 0) {
    xml = rawData.toString("utf-8");
  } else {
    // Deflate — use zlib inflateRawSync
    try {
      const zlib = require("zlib");
      xml = zlib.inflateRawSync(rawData).toString("utf-8");
    } catch { return ""; }
  }

  // Strip XML tags, keep text
  return xml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
}
