import { randomUUID } from "crypto";
import { mkdir, writeFile } from "fs/promises";
import path from "path";
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "@/auth";
import { CONTENT_ROLES, hasAnyRole } from "@/lib/admin-auth";

export const runtime = "nodejs";

const maxUploadSize = 5 * 1024 * 1024;
const maxModelUploadSize = 2 * 1024 * 1024 * 1024;
const imageTypes = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
const modelTypes = new Set([
  "model/gltf-binary",
  "model/gltf+json",
  "model/gltf",
  "model/fbx",
  "application/vnd.autodesk.fbx",
  "application/x-fbx",
  "model/obj",
  "application/x-tgif",
  "model/stl",
  "application/sla",
  "application/vnd.ms-pki.stl",
  "model/vnd.usdz+zip",
  "application/vnd.usdz+zip",
]);

function extensionFor(file: File) {
  const fromName = path.extname(file.name).toLowerCase();

  if (fromName && [".jpg", ".jpeg", ".png", ".webp", ".gif"].includes(fromName)) {
    return fromName;
  }

  if (file.type === "image/jpeg") return ".jpg";
  if (file.type === "image/png") return ".png";
  if (file.type === "image/webp") return ".webp";
  if (file.type === "image/gif") return ".gif";

  return ".png";
}

function modelExtensionFor(file: File) {
  const fromName = path.extname(file.name).toLowerCase();
  if ([".glb", ".gltf", ".usdz", ".fbx", ".obj", ".stl"].includes(fromName)) return fromName;
  if (file.type.includes("usdz")) return ".usdz";
  if (file.type.includes("fbx")) return ".fbx";
  if (file.type.includes("obj")) return ".obj";
  if (file.type.includes("stl")) return ".stl";
  if (file.type.includes("gltf")) return ".gltf";
  return ".glb";
}

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);

  if (!session?.user || !hasAnyRole(session.user.role, CONTENT_ROLES)) {
    return NextResponse.json({ ok: false, message: "Unauthorized" }, { status: 401 });
  }

  const formData = await request.formData();
  const file = formData.get("file");
  const kind = String(formData.get("kind") ?? "image");

  if (!(file instanceof File)) {
    return NextResponse.json({ ok: false, message: "Choose a file to upload." }, { status: 400 });
  }

  const isModelUpload = kind === "model";
  const fromName = path.extname(file.name).toLowerCase();

  if (isModelUpload) {
    const validModelByExt = [".glb", ".gltf", ".usdz", ".fbx", ".obj", ".stl"].includes(fromName);
    const validModelByType = !file.type || modelTypes.has(file.type);
    if (!validModelByExt || !validModelByType) {
      return NextResponse.json(
        { ok: false, message: "Allowed 3D formats: GLB, GLTF, USDZ, FBX, OBJ, STL." },
        { status: 400 },
      );
    }
    if (file.size > maxModelUploadSize) {
      return NextResponse.json({ ok: false, message: "3D file must be smaller than 2 GB." }, { status: 400 });
    }
  } else {
    if (!imageTypes.has(file.type)) {
      return NextResponse.json({ ok: false, message: "Only JPG, PNG, WebP, or GIF images are allowed." }, { status: 400 });
    }
    if (file.size > maxUploadSize) {
      return NextResponse.json({ ok: false, message: "Image must be smaller than 5 MB." }, { status: 400 });
    }
  }

  const uploadDir = path.join(
    process.cwd(),
    "public",
    "uploads",
    "admin",
    isModelUpload ? "models" : "images",
  );
  await mkdir(uploadDir, { recursive: true });

  const filename = `${randomUUID()}${isModelUpload ? modelExtensionFor(file) : extensionFor(file)}`;
  const filepath = path.join(uploadDir, filename);
  const bytes = await file.arrayBuffer();

  await writeFile(filepath, Buffer.from(bytes));

  return NextResponse.json({
    ok: true,
    url: `/uploads/admin/${isModelUpload ? "models" : "images"}/${filename}`,
  });
}
