import { Prisma } from "@prisma/client";
import { NextResponse } from "next/server";
import { requireAdminApiUser } from "@/lib/auth/admin";
import { analyzePdf } from "@/lib/pdf/analyzePdf";
import { prisma } from "@/lib/prisma";
import { contractCreateInputFromExtraction } from "@/lib/contracts/server";
import { extractContractWithAi } from "@/lib/contracts/extraction";
import { MAX_CONTRACT_PDF_BYTES, storeContractDocumentFile } from "@/lib/contracts/storage";
import {
  ensureProposalPdfLinkedToContract,
  getLatestProposalPdfSourceForUser,
} from "@/lib/contracts/proposal-continuity";

export const runtime = "nodejs";

function isPdfFile(file: File) {
  return file.type === "application/pdf" || file.name.toLowerCase().endsWith(".pdf");
}

function removePdfExtension(fileName: string) {
  return fileName.replace(/\.pdf$/i, "").trim();
}

function logContinuityHandoff(event: string, data: Record<string, unknown>) {
  console.info(
    "[continuity_handoff]",
    JSON.stringify({
      event,
      at: new Date().toISOString(),
      ...data,
    }),
  );
}

export async function POST(request: Request) {
  const user = await requireAdminApiUser();

  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const formData = await request.formData();
  const contractIdRaw = formData.get("contractId");
  const contractId = typeof contractIdRaw === "string" ? contractIdRaw.trim() : "";
  const sourceProposalIdRaw = formData.get("sourceProposalId");
  const sourceProposalId = typeof sourceProposalIdRaw === "string" ? sourceProposalIdRaw.trim() : "";
  const fileValue = formData.get("file");
  const uploadedFile = fileValue instanceof File ? fileValue : null;

  logContinuityHandoff("m5_to_m8_extract_requested", {
    userId: user.id,
    contractId: contractId || null,
    sourceProposalId: sourceProposalId || null,
    hasUploadedFile: Boolean(uploadedFile),
  });

  let sourceProposalLookup: Awaited<ReturnType<typeof getLatestProposalPdfSourceForUser>> | null = null;
  let resolvedSourceProposalId: string | null = null;
  if (sourceProposalId) {
    try {
      sourceProposalLookup = await getLatestProposalPdfSourceForUser(user.id, sourceProposalId);
    } catch {
      return NextResponse.json({ error: "No fue posible leer el PDF de la propuesta fuente." }, { status: 400 });
    }

    if (sourceProposalLookup.status === "proposal_not_found") {
      return NextResponse.json({ error: "Propuesta fuente no encontrada." }, { status: 404 });
    }

    resolvedSourceProposalId = sourceProposalId;
    logContinuityHandoff("m5_to_m8_source_proposal_resolved", {
      userId: user.id,
      sourceProposalId: resolvedSourceProposalId,
      lookupStatus: sourceProposalLookup.status,
    });
  }

  let fileName = "";
  let mimeType = "application/pdf";
  let fileBuffer: Buffer;

  if (uploadedFile) {
    if (uploadedFile.size <= 0) {
      return NextResponse.json({ error: "El archivo esta vacio." }, { status: 400 });
    }

    if (uploadedFile.size > MAX_CONTRACT_PDF_BYTES) {
      return NextResponse.json({ error: "El archivo excede el limite de 20MB." }, { status: 400 });
    }

    if (!isPdfFile(uploadedFile)) {
      return NextResponse.json({ error: "Solo se permite PDF en extraccion de contrato." }, { status: 400 });
    }

    fileName = uploadedFile.name;
    mimeType = uploadedFile.type || "application/pdf";
    fileBuffer = Buffer.from(await uploadedFile.arrayBuffer());
  } else {
    if (!sourceProposalLookup) {
      return NextResponse.json({ error: "Debes adjuntar un contrato PDF o seleccionar una propuesta de Modulo 5." }, { status: 400 });
    }

    if (sourceProposalLookup.status !== "ok") {
      return NextResponse.json({ error: "La propuesta fuente no tiene un PDF disponible para reutilizar." }, { status: 400 });
    }

    fileName = sourceProposalLookup.source.document.fileName;
    mimeType = sourceProposalLookup.source.document.mimeType;
    fileBuffer = sourceProposalLookup.source.fileBuffer;
  }

  const analyzed = await analyzePdf(fileBuffer);
  const extraction = await extractContractWithAi(analyzed.text);

  let contract = null as { id: string } | null;

  if (contractId) {
    const existing = await prisma.contractRecord.findFirst({
      where: {
        id: contractId,
        userId: user.id,
      },
      select: {
        id: true,
      },
    });

    if (!existing) {
      return NextResponse.json({ error: "Contrato no encontrado." }, { status: 404 });
    }

    const base = contractCreateInputFromExtraction({
      extraction,
      fallbackTitle: removePdfExtension(fileName) || "Contrato sin titulo",
    });

    await prisma.contractRecord.update({
      where: {
        id: existing.id,
      },
      data: {
        title: extraction.fields.title ? base.title : undefined,
        counterpartyEntity: extraction.fields.counterpartyEntity ? base.counterpartyEntity : undefined,
        contractNumber: extraction.fields.contractNumber === null ? undefined : base.contractNumber,
        contractType: extraction.fields.contractType ? base.contractType : undefined,
        startDate: extraction.fields.startDate ? new Date(extraction.fields.startDate) : undefined,
        endDate: extraction.fields.endDate ? new Date(extraction.fields.endDate) : undefined,
        totalAmount: extraction.fields.totalAmount === null ? undefined : base.totalAmount,
        currency: base.currency,
        description: extraction.fields.description ? base.description : undefined,
        sourceProposalId: resolvedSourceProposalId ?? undefined,
      },
    });

    contract = existing;
  } else {
    const base = contractCreateInputFromExtraction({
      extraction,
      fallbackTitle: removePdfExtension(fileName) || "Contrato sin titulo",
    });

    contract = await prisma.contractRecord.create({
      data: {
        userId: user.id,
        sourceProposalId: resolvedSourceProposalId,
        title: base.title,
        counterpartyEntity: base.counterpartyEntity,
        contractNumber: base.contractNumber,
        contractType: base.contractType,
        startDate: base.startDate ? new Date(base.startDate) : null,
        endDate: base.endDate ? new Date(base.endDate) : null,
        totalAmount: base.totalAmount,
        currency: base.currency,
        status: base.status,
        description: base.description,
      },
      select: {
        id: true,
      },
    });
  }

  const targetContractId = contract.id;
  logContinuityHandoff("m5_to_m8_contract_target_resolved", {
    userId: user.id,
    targetContractId,
    sourceProposalId: resolvedSourceProposalId,
    mode: contractId ? "update" : "create",
  });

  const [deliverableCount, paymentCount] = await Promise.all([
    prisma.contractDeliverable.count({
      where: {
        contractId: targetContractId,
      },
    }),
    prisma.contractPayment.count({
      where: {
        contractId: targetContractId,
      },
    }),
  ]);

  if (deliverableCount === 0 && extraction.deliverables.length > 0) {
    await prisma.contractDeliverable.createMany({
      data: extraction.deliverables.map((item) => ({
        contractId: targetContractId,
        title: item.title,
        dueDate: item.dueDate ? new Date(item.dueDate) : null,
        amountLinked: item.amountLinked,
        notes: item.notes ?? "Detectado automaticamente por IA.",
        status: "PENDING",
      })),
    });
  }

  if (paymentCount === 0 && extraction.paymentMilestones.length > 0) {
    await prisma.contractPayment.createMany({
      data: extraction.paymentMilestones
        .filter((item) => item.paymentDate)
        .map((item) => ({
          contractId: targetContractId,
          amount: item.amount ?? 0,
          paymentDate: new Date(item.paymentDate as string),
          concept: item.concept,
          status: "REGISTERED",
        })),
    });
  }

  let uploadedDocumentId: string | null = null;
  if (uploadedFile) {
    const stored = await storeContractDocumentFile(user.id, targetContractId, fileName, mimeType, fileBuffer);
    const createdDocument = await prisma.contractDocument.create({
      data: {
        contractId: targetContractId,
        fileName: stored.fileName,
        filePath: stored.filePath,
        mimeType: stored.mimeType,
        sizeBytes: stored.sizeBytes,
        checksumSha256: stored.checksumSha256,
        kind: "SIGNED_CONTRACT",
      },
      select: {
        id: true,
      },
    });
    uploadedDocumentId = createdDocument.id;
  } else if (sourceProposalLookup?.status === "ok") {
    const linked = await ensureProposalPdfLinkedToContract({
      userId: user.id,
      contractId: targetContractId,
      source: sourceProposalLookup.source,
    });
    uploadedDocumentId = linked.documentId;
  }

  await prisma.contractExtractionHistory.create({
    data: {
      contractId: targetContractId,
      userId: user.id,
      engine: extraction.engine,
      model: extraction.model,
      resultJson: extraction as unknown as Prisma.InputJsonValue,
      warningsJson: extraction.warnings as unknown as Prisma.InputJsonValue,
    },
  });

  const fullContract = await prisma.contractRecord.findUnique({
    where: { id: targetContractId },
    include: {
      deliverables: {
        orderBy: [{ dueDate: "asc" }, { createdAt: "asc" }],
      },
      payments: {
        orderBy: [{ paymentDate: "desc" }, { createdAt: "desc" }],
      },
      documents: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
  });

  return NextResponse.json({
    ok: true,
    contract: fullContract,
    extraction: {
      ...extraction,
      analyzed: {
        methodUsed: analyzed.methodUsed,
        numPages: analyzed.numPages,
        warnings: analyzed.warnings,
      },
    },
    uploadedDocumentId,
  });
}
