import { NextRequest, NextResponse } from "next/server";
import { Privacy } from "@prisma/client";
import { requireApiUser } from "@/lib/api-auth";
import { createVideoJob, saveUploadedFile } from "@/lib/jobs/create-job";
import {
  applyCreatePlaylistToItems,
  parseCreatePlaylistInput,
} from "@/lib/jobs/resolve-playlist";
import { filenameWithoutExtension } from "@/lib/constants";
import { mapWithConcurrency } from "@/lib/fs-utils";
import { getPlanLimits } from "@/lib/plans";
import { checkQuota } from "@/lib/quota";
import type { CreateJobPayload, CreatePlaylistRequest, ItemMetadata } from "@/lib/types";

export const maxDuration = 300;

type BatchItemInput = Partial<ItemMetadata> & {
  filename?: string;
  title?: string;
};

function defaultMetadata(title: string): ItemMetadata {
  return {
    title,
    songTitle: null,
    description: "",
    tags: "",
    privacy: "PUBLIC",
    categoryId: "10",
    resolution: "1920x1080",
    notifySubscribers: true,
    madeForKids: false,
    embeddable: true,
    creativeCommons: false,
    includeWatermark: false,
    playlistId: null,
    artist: null,
  };
}

export async function POST(req: NextRequest) {
  try {
    const { error, user } = await requireApiUser(req);
    if (error || !user) return error!;

    const formData = await req.formData();
    const image = formData.get("image") as File | null;
    const audioFiles = formData.getAll("audio").filter((f): f is File => f instanceof File);
    const metadataRaw = formData.get("metadata");

    if (!image || audioFiles.length === 0) {
      return NextResponse.json(
        { error: "Provide multipart fields: image (file), audio (one or more files)" },
        { status: 400 },
      );
    }

    const limits = getPlanLimits();
    if (audioFiles.length > limits.maxBatchSize) {
      return NextResponse.json(
        { error: `Batch limit exceeded. Max ${limits.maxBatchSize} files per batch.` },
        { status: 400 },
      );
    }

    const quotaCheck = await checkQuota(user.id, audioFiles.length);
    if (!quotaCheck.ok) {
      return NextResponse.json({ error: quotaCheck.error }, { status: 403 });
    }

    let itemMeta: BatchItemInput[] = [];
    let defaults: Partial<ItemMetadata> = {};
    let createPlaylist: CreatePlaylistRequest | null = null;

    if (metadataRaw) {
      try {
        const parsed = JSON.parse(String(metadataRaw)) as {
          items?: BatchItemInput[];
          defaults?: Partial<ItemMetadata>;
          createPlaylist?: unknown;
        };
        itemMeta = parsed.items ?? [];
        defaults = parsed.defaults ?? {};
        createPlaylist = parseCreatePlaylistInput(parsed.createPlaylist);
        if (parsed.createPlaylist && !createPlaylist) {
          return NextResponse.json(
            {
              error:
                "createPlaylist requires a title. Optional: description, privacy (public|unlisted|private)",
            },
            { status: 400 },
          );
        }
      } catch {
        return NextResponse.json({ error: "metadata must be valid JSON" }, { status: 400 });
      }
    }

    const sessionKey = Date.now().toString();
    const imageUpload = await saveUploadedFile(user.id, image, "image", { sessionKey });

    const uploads = await mapWithConcurrency(audioFiles, 4, (audio) =>
      saveUploadedFile(user.id, audio, "audio", { sessionKey }),
    );

    const builtItems: CreateJobPayload["items"] = uploads.map((upload, i) => {
      const audio = audioFiles[i];
      const metaInput = itemMeta[i] ?? itemMeta.find((m) => m.filename === audio.name) ?? {};
      const base = defaultMetadata(
        metaInput.title?.trim() || filenameWithoutExtension(audio.name),
      );
      const metadata: ItemMetadata = {
        ...base,
        ...defaults,
        ...metaInput,
        title: (metaInput.title ?? defaults.title ?? base.title).trim(),
        privacy: (metaInput.privacy ?? defaults.privacy ?? base.privacy) as Privacy,
      };

      return {
        audioPath: upload.path,
        audioFilename: upload.filename,
        metadata,
      };
    });

    const { items, playlist } = await applyCreatePlaylistToItems(user, builtItems, createPlaylist);

    const job = await createVideoJob(user, {
      imagePath: imageUpload.path,
      items,
    });

    return NextResponse.json({
      jobId: job.id,
      itemCount: job.items.length,
      status: job.status,
      playlist: playlist
        ? { id: playlist.id, title: playlist.title, privacy: playlist.privacy }
        : null,
    });
  } catch (err) {
    console.error("Batch upload failed:", err);
    const message = err instanceof Error ? err.message : "Batch upload failed";
    const status = message.includes("Batch limit exceeded") ? 400 : 500;
    return NextResponse.json({ error: message }, { status });
  }
}
