/**
 * POST /api/upload/image
 *
 * Universal image upload endpoint. Every surface (forum composer, PR
 * submission, contributor article, featured-story admin, avatar update,
 * banner upload, anything else) POSTs here.
 *
 * Auth: requires a logged-in Supabase user. Anonymous uploads are
 * rejected to keep the bucket from being abused as a public file dump.
 *
 * Pipeline (see lib/images/optimize.ts):
 *   - sharp resize to ≤4096px longest side
 *   - AVIF + WebP + format-preserving fallback (JPEG/PNG) at q=82
 *   - 320px thumbnail
 *
 * Storage: writes to the `bb-media` Supabase bucket under
 *   /<user_id>/<yyyy>/<mm>/<random>.{webp,avif,jpg,png}
 * and returns the public URLs + dimensions so the client can either
 * embed an <img srcset> or paste a markdown ![alt](url) into their post.
 */
import { NextRequest, NextResponse } from "next/server";
import { randomBytes } from "node:crypto";
import { createClient as createServer } from "@/lib/supabase/server";
import { createClient as createService } from "@supabase/supabase-js";
import { optimizeImage } from "@/lib/images/optimize";

const BUCKET = "bb-media";
const MAX_INPUT_BYTES = 20 * 1024 * 1024; // 20 MB hard cap on input

export async function POST(req: NextRequest) {
  // ── 1. Auth ──────────────────────────────────────────────────────────────
  const sb = await createServer();
  const { data: { user } } = await sb.auth.getUser();
  if (!user) {
    return NextResponse.json({ error: "Not signed in" }, { status: 401 });
  }

  // ── 2. Read upload ───────────────────────────────────────────────────────
  let formData: FormData;
  try {
    formData = await req.formData();
  } catch {
    return NextResponse.json({ error: "Expected multipart/form-data" }, { status: 400 });
  }
  const file = formData.get("file");
  if (!(file instanceof File)) {
    return NextResponse.json({ error: "Missing file" }, { status: 400 });
  }
  if (file.size > MAX_INPUT_BYTES) {
    return NextResponse.json(
      { error: `Image too large (${(file.size / 1024 / 1024).toFixed(1)} MB). Max 20 MB.` },
      { status: 413 },
    );
  }
  if (!file.type.startsWith("image/")) {
    return NextResponse.json({ error: "Not an image" }, { status: 415 });
  }

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

  // ── 3. Optimize ──────────────────────────────────────────────────────────
  let opt;
  try {
    opt = await optimizeImage(input);
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : "image decode failed";
    return NextResponse.json({ error: `Could not process image: ${msg}` }, { status: 422 });
  }

  // ── 4. Upload derivatives ────────────────────────────────────────────────
  const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
  const key = process.env.SUPABASE_SERVICE_ROLE_KEY || "";
  if (!url || !key) {
    return NextResponse.json({ error: "Storage not configured" }, { status: 500 });
  }
  const svc = createService(url, key, { auth: { persistSession: false } });

  const now = new Date();
  const dir = `${user.id}/${now.getUTCFullYear()}/${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
  const stub = randomBytes(8).toString("hex");

  async function upload(suffix: string, body: Buffer, contentType: string): Promise<string | null> {
    const path = `${dir}/${stub}${suffix}`;
    const { error } = await svc.storage.from(BUCKET).upload(path, body, {
      contentType, upsert: false, cacheControl: "31536000, immutable",
    });
    if (error) {
      console.error("storage upload failed", path, error);
      return null;
    }
    return svc.storage.from(BUCKET).getPublicUrl(path).data.publicUrl;
  }

  const urls: Record<string, string | null> = {};
  if (opt.avif)       urls.avif      = await upload(".avif",      opt.avif,      "image/avif");
  /* webp is mandatory */ urls.webp  = await upload(".webp",      opt.webp,      "image/webp");
  if (opt.jpeg)       urls.jpeg      = await upload(".jpg",       opt.jpeg,      "image/jpeg");
  if (opt.png)        urls.png       = await upload(".png",       opt.png,       "image/png");
  if (opt.thumbnail)  urls.thumbnail = await upload("_thumb.jpg", opt.thumbnail, "image/jpeg");

  if (!urls.webp) {
    return NextResponse.json({ error: "Storage upload failed" }, { status: 500 });
  }

  // ── 5. Return URLs + metadata ───────────────────────────────────────────
  return NextResponse.json({
    ok: true,
    width: opt.width,
    height: opt.height,
    format: opt.format,
    hasAlpha: opt.hasAlpha,
    urls,
    bytes: opt.bytes,
    // Convenience: the most-compatible URL the caller should embed by default.
    // Browsers that support AVIF will pick it via <picture>; this URL is the
    // safe-default <img src=>.
    primaryUrl: urls.webp,
  });
}
