import { NextRequest } from "next/server";
import { mkdir, writeFile } from "node:fs/promises";
import { extname, join } from "node:path";
import { randomBytes } from "node:crypto";
import { apiError, apiOk, requireApiToken } from "@/lib/api-auth";
import { prisma } from "@/lib/db";

export const runtime = "nodejs";

const MAX_BYTES = 10 * 1024 * 1024; // 10MB
const ALLOWED_MIME = new Set([
  "image/jpeg",
  "image/png",
  "image/webp",
  "image/avif",
  "image/gif",
  "image/svg+xml",
]);

const UPLOAD_DIR = process.env.MEDIA_UPLOAD_DIR ?? "public/uploads";
const PUBLIC_BASE = process.env.MEDIA_PUBLIC_BASE ?? "/uploads";

function safeFilename(orig: string): string {
  const base = orig
    .replace(/[^\w.-]+/g, "-")
    .replace(/-+/g, "-")
    .toLowerCase();
  const ext = extname(base) || ".bin";
  const stem = base.slice(0, base.length - ext.length).slice(0, 60) || "file";
  const rand = randomBytes(4).toString("hex");
  const yymm = new Date().toISOString().slice(2, 7).replace("-", "");
  return `${yymm}/${stem}-${rand}${ext}`;
}

// POST /api/v1/media — multipart/form-data, field: file
export async function POST(req: NextRequest) {
  const ctx = await requireApiToken(req, ["media:upload"]);
  if (ctx instanceof Response) return ctx;

  const ct = req.headers.get("content-type") ?? "";
  if (!ct.startsWith("multipart/form-data"))
    return apiError(415, "bad_content_type", "Cần multipart/form-data với field 'file'.");

  let form: FormData;
  try {
    form = await req.formData();
  } catch {
    return apiError(400, "bad_form", "Form data parse fail.");
  }
  const file = form.get("file");
  const alt = (form.get("alt") as string | null)?.trim() ?? null;
  if (!(file instanceof File))
    return apiError(400, "missing_file", "Thiếu field 'file'.");
  if (file.size > MAX_BYTES)
    return apiError(413, "too_large", `File vượt ${MAX_BYTES / 1024 / 1024}MB.`);
  if (!ALLOWED_MIME.has(file.type))
    return apiError(415, "bad_mime", `MIME không hỗ trợ: ${file.type}`);

  const rel = safeFilename(file.name || "upload");
  const absDir = join(UPLOAD_DIR, rel.split("/")[0]);
  await mkdir(absDir, { recursive: true });
  const abs = join(UPLOAD_DIR, rel);
  const buf = Buffer.from(await file.arrayBuffer());
  await writeFile(abs, buf);
  const url = `${PUBLIC_BASE.replace(/\/$/, "")}/${rel}`;

  const media = await prisma.media.create({
    data: {
      filename: file.name,
      url,
      mime: file.type,
      size: file.size,
      alt,
      uploadedBy: ctx.userId,
    },
  });
  await ctx.audit({
    action: "media.upload",
    target: media.id,
    meta: { url, size: file.size, mime: file.type },
  });
  return apiOk({ id: media.id, url: media.url, size: media.size, mime: media.mime }, 201);
}

// GET /api/v1/media?page=&pageSize=
export async function GET(req: NextRequest) {
  const ctx = await requireApiToken(req, ["post:read"]);
  if (ctx instanceof Response) return ctx;
  const url = new URL(req.url);
  const page = Math.max(1, Number(url.searchParams.get("page") ?? 1));
  const pageSize = Math.min(
    100,
    Math.max(1, Number(url.searchParams.get("pageSize") ?? 30)),
  );
  const [items, total] = await Promise.all([
    prisma.media.findMany({
      orderBy: { createdAt: "desc" },
      skip: (page - 1) * pageSize,
      take: pageSize,
    }),
    prisma.media.count(),
  ]);
  return apiOk({ page, pageSize, total, items });
}
