import { writeFile, unlink } from "fs/promises";
import path from "path";
import { NextResponse } from "next/server";
import { DepartmentKey } from "@prisma/client";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import {
  createStoredFilename,
  ensureProjectUploadDir,
  getEvidenceKindFromMimeType,
  isAllowedMimeType,
  MAX_UPLOAD_BYTES,
  PROJECT_UPLOAD_DIR,
} from "@/lib/projects/capture";
import { validateCaptureAccess } from "@/lib/projects/capture-permissions";
import { canAccessProjects, mapProjectCaptureEvidence } from "@/lib/projects/persistence";

function isUploadLikeFile(value: FormDataEntryValue | null): value is File {
  if (!value || typeof value === "string") {
    return false;
  }

  const candidate = value as Partial<File>;
  return typeof candidate.size === "number" && typeof candidate.type === "string" && typeof candidate.arrayBuffer === "function";
}

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);
  if (!session?.user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!canAccessProjects(session.user.role, session.user.department)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const formData = await request.formData();
  const title = String(formData.get("title") ?? "").trim();
  const note = String(formData.get("note") ?? "").trim() || null;
  const initiativeId = String(formData.get("initiativeId") ?? "").trim() || undefined;
  const taskId = String(formData.get("taskId") ?? "").trim() || undefined;
  const fileValue = formData.get("file");

  if (!title) {
    return NextResponse.json({ error: "title is required." }, { status: 400 });
  }

  if (!isUploadLikeFile(fileValue)) {
    return NextResponse.json({ error: "file is required." }, { status: 400 });
  }

  if (fileValue.size <= 0) {
    return NextResponse.json({ error: "file is empty." }, { status: 400 });
  }

  if (fileValue.size > MAX_UPLOAD_BYTES) {
    return NextResponse.json({ error: "file exceeds 10 MB limit." }, { status: 400 });
  }

  if (!isAllowedMimeType(fileValue.type)) {
    return NextResponse.json({ error: "Unsupported file type." }, { status: 400 });
  }

  const access = await validateCaptureAccess({
    userId: session.user.id,
    role: session.user.role,
    department: session.user.department,
    initiativeId,
    taskId,
  });

  if (!access.ok) {
    return NextResponse.json({ error: access.error }, { status: access.status });
  }

  await ensureProjectUploadDir();

  const storagePath = createStoredFilename(fileValue.type);
  const absolutePath = path.join(PROJECT_UPLOAD_DIR, storagePath);
  const bytes = Buffer.from(await fileValue.arrayBuffer());

  try {
    await writeFile(absolutePath, bytes);

    const record = await prisma.projectCaptureEvidence.create({
      data: {
        department: DepartmentKey.proyectos,
        initiativeId: access.initiativeId,
        taskId: access.taskId,
        uploadedById: session.user.id,
        kind: getEvidenceKindFromMimeType(fileValue.type),
        title,
        note,
        storagePath,
        mimeType: fileValue.type,
        sizeBytes: fileValue.size,
      },
      include: {
        uploadedBy: {
          select: {
            name: true,
            email: true,
          },
        },
      },
    });

    return NextResponse.json({
      ok: true,
      evidence: mapProjectCaptureEvidence(record),
    });
  } catch (error) {
    await unlink(absolutePath).catch(() => {
      // ignore cleanup failure
    });

    const message = error instanceof Error ? error.message : "Failed to upload evidence.";
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
