"use server";

import { unlink } from "fs/promises";
import path from "path";
import { revalidatePath } from "next/cache";
import { hasPermission } from "@/lib/adminAuth";
import { connectDB } from "@/lib/db";
import { PortfolioItem } from "@/models/PortfolioItem";
import { PortfolioCategory } from "@/models/PortfolioCategory";
import { savePortfolioImage } from "@/lib/uploads";

const PORTFOLIO_ADMIN_PATH = "/de/admin/portfolio";
const MAX_FEATURED = 3;

type FormState = { error?: string };

function parseList(raw: FormDataEntryValue | null): string[] {
  return String(raw || "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);
}

// Windows-Zeilenumbrüche (\r\n) auf \n normalisieren — sonst erkennt die
// Absatz-Trennung in PortfolioGallery.tsx (excerptDescription) Absätze
// nicht zuverlässig und blendet ganze Beschreibungen fälschlich aus.
function normalizeText(raw: FormDataEntryValue | null): string {
  return String(raw || "").replace(/\r\n/g, "\n").trim();
}

function parseDate(value: FormDataEntryValue | null): Date | null {
  const s = String(value || "").trim();
  if (!s) return null;
  const d = new Date(s);
  return Number.isNaN(d.getTime()) ? null : d;
}

// Nur Dateien unter /uploads/portfolio/ löschen (unsere eigenen Uploads) —
// ältere/importierte Bildpfade außerhalb dieses Ordners bleiben unangetastet.
async function tryDeletePortfolioFile(imagePath: string) {
  if (!imagePath.startsWith("/uploads/portfolio/")) return;
  try {
    await unlink(path.join(process.cwd(), "public", imagePath));
  } catch {
    // Datei war schon weg — nicht schlimm.
  }
}

export async function createPortfolioItemAction(_prev: FormState, formData: FormData): Promise<FormState> {
  if (!(await hasPermission("portfolio_create"))) return { error: "Keine Berechtigung." };
  const title = String(formData.get("title") || "").trim();
  const category = String(formData.get("category") || "").trim() || "Cosplay";
  if (!title) return { error: "Bitte einen Titel eingeben." };

  const files = formData.getAll("images").filter((f): f is File => f instanceof File && f.size > 0);
  if (files.length === 0) return { error: "Bitte mindestens ein Bild hochladen." };

  let images: string[];
  try {
    images = await Promise.all(files.map((f) => savePortfolioImage(f)));
  } catch (err) {
    return { error: err instanceof Error ? err.message : "Upload fehlgeschlagen." };
  }

  // Index des in der Vorschau ausgewählten Vorschaubilds — fällt auf das
  // erste Bild zurück, falls nichts (gültiges) übermittelt wurde.
  const coverIndexRaw = Number(formData.get("coverIndex"));
  const coverIndex = Number.isInteger(coverIndexRaw) && coverIndexRaw >= 0 && coverIndexRaw < images.length ? coverIndexRaw : 0;

  await connectDB();
  await PortfolioItem.create({
    title,
    titleEn: String(formData.get("titleEn") || "").trim(),
    category,
    images,
    coverImage: images[coverIndex],
    description: normalizeText(formData.get("description")),
    descriptionEn: normalizeText(formData.get("descriptionEn")),
    eventLabel: String(formData.get("eventLabel") || "").trim(),
    modelInstagram: parseList(formData.get("modelInstagram")),
    tags: parseList(formData.get("tags")),
    visible: formData.get("visible") === "on",
    featured: false,
    publishedAt: parseDate(formData.get("publishedAt")) || new Date(),
    sortOrder: Date.now(),
  });

  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
  return {};
}

export async function updatePortfolioItemAction(itemId: string, formData: FormData) {
  if (!(await hasPermission("portfolio_edit"))) return;
  const title = String(formData.get("title") || "").trim();
  if (!title) return;

  await connectDB();
  const item = await PortfolioItem.findById(itemId);
  if (!item) return;

  const removeImages = new Set(formData.getAll("removeImages").map(String));
  let images = item.images.filter((img) => !removeImages.has(img));
  for (const img of removeImages) await tryDeletePortfolioFile(img);

  const newFiles = formData.getAll("newImages").filter((f): f is File => f instanceof File && f.size > 0);
  if (newFiles.length > 0) {
    const uploaded = await Promise.all(newFiles.map((f) => savePortfolioImage(f)));
    images = [...images, ...uploaded];
  }
  if (images.length === 0) return; // mindestens ein Bild muss bleiben

  // Fällt auf das erste verbleibende Bild zurück, falls das gewählte
  // Vorschaubild gerade mit entfernt wurde oder gar keins übermittelt kam.
  const submittedCover = String(formData.get("coverImage") || "");
  const coverImage = images.includes(submittedCover) ? submittedCover : images[0];

  item.title = title;
  item.titleEn = String(formData.get("titleEn") || "").trim();
  item.category = String(formData.get("category") || "").trim() || "Cosplay";
  item.images = images;
  item.coverImage = coverImage;
  item.description = normalizeText(formData.get("description"));
  item.descriptionEn = normalizeText(formData.get("descriptionEn"));
  item.eventLabel = String(formData.get("eventLabel") || "").trim();
  item.modelInstagram = parseList(formData.get("modelInstagram"));
  item.tags = parseList(formData.get("tags"));
  item.visible = formData.get("visible") === "on";
  const pub = parseDate(formData.get("publishedAt"));
  if (pub) item.publishedAt = pub;
  await item.save();

  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
}

// Startseite zeigt genau die "Featured"-Beiträge (max. 3) — Versuch, einen
// vierten zu markieren, wird stillschweigend ignoriert; die Checkbox springt
// nach dem Neuladen zurück, das signalisiert den Grenzwert implizit.
export async function setFeaturedAction(itemId: string, featured: boolean) {
  if (!(await hasPermission("portfolio_edit"))) return;
  await connectDB();
  if (featured) {
    const count = await PortfolioItem.countDocuments({ featured: true, _id: { $ne: itemId } });
    if (count >= MAX_FEATURED) return;
  }
  await PortfolioItem.findByIdAndUpdate(itemId, { featured });
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
}

export async function setVisibleAction(itemId: string, visible: boolean) {
  if (!(await hasPermission("portfolio_edit"))) return;
  await connectDB();
  await PortfolioItem.findByIdAndUpdate(itemId, { visible });
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
}

export async function movePortfolioItemAction(itemId: string, direction: "up" | "down") {
  if (!(await hasPermission("portfolio_edit"))) return;
  await connectDB();
  const item = await PortfolioItem.findById(itemId).lean();
  if (!item) return;

  const neighbor = await PortfolioItem.findOne(
    direction === "up" ? { sortOrder: { $lt: item.sortOrder } } : { sortOrder: { $gt: item.sortOrder } }
  )
    .sort({ sortOrder: direction === "up" ? -1 : 1 })
    .lean();
  if (!neighbor) return;

  await Promise.all([
    PortfolioItem.findByIdAndUpdate(item._id, { sortOrder: neighbor.sortOrder }),
    PortfolioItem.findByIdAndUpdate(neighbor._id, { sortOrder: item.sortOrder }),
  ]);
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
}

export async function deletePortfolioItemAction(itemId: string) {
  if (!(await hasPermission("portfolio_delete"))) return;
  await connectDB();
  const item = await PortfolioItem.findById(itemId);
  if (!item) return;
  for (const img of item.images) await tryDeletePortfolioFile(img);
  await item.deleteOne();
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
}

// Liefert die verwaltbare Kategorienliste, sortiert wie in der Admin-Ansicht
// festgelegt — und übernimmt einmalig alle Kategorienamen, die auf
// bestehenden Beiträgen schon (frei getippt) stehen, aber noch keinen
// eigenen Eintrag haben. Sonst würde beim Umstieg auf das Dropdown eine
// bisherige Kategorie plötzlich nirgends mehr auswählbar sein.
export async function getPortfolioCategories() {
  await connectDB();
  const [existing, usedNames] = await Promise.all([
    PortfolioCategory.find({}).lean(),
    PortfolioItem.distinct("category"),
  ]);
  const existingNames = new Set(existing.map((c) => c.name));
  const missing = usedNames.filter((n): n is string => Boolean(n) && !existingNames.has(n));
  if (missing.length) {
    const base = Date.now();
    await PortfolioCategory.insertMany(missing.map((name, i) => ({ name, sortOrder: base + i })));
  }
  return PortfolioCategory.find({}).sort({ sortOrder: 1 }).lean();
}

type CategoryFormState = { error?: string };

export async function createPortfolioCategoryAction(_prev: CategoryFormState, formData: FormData): Promise<CategoryFormState> {
  if (!(await hasPermission("portfolio_create"))) return { error: "Keine Berechtigung." };
  const name = String(formData.get("name") || "").trim();
  if (!name) return { error: "Bitte einen Namen eingeben." };

  await connectDB();
  const existing = await PortfolioCategory.findOne({ name });
  if (existing) return { error: "Diese Kategorie gibt es schon." };

  await PortfolioCategory.create({ name, sortOrder: Date.now() });
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  return {};
}

export async function renamePortfolioCategoryAction(categoryId: string, formData: FormData) {
  if (!(await hasPermission("portfolio_edit"))) return;
  const name = String(formData.get("name") || "").trim();
  if (!name) return;

  await connectDB();
  const category = await PortfolioCategory.findById(categoryId);
  if (!category) return;
  const oldName = category.name;
  category.name = name;
  await category.save();

  // Bestehende Beiträge mit der alten Bezeichnung auf die neue umziehen,
  // sonst würden sie plötzlich einer nicht mehr existierenden Kategorie
  // zugeordnet sein.
  if (oldName !== name) await PortfolioItem.updateMany({ category: oldName }, { category: name });

  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
  revalidatePath("/de");
}

// Löscht nur den verwaltbaren Kategorie-Eintrag — bestehende Beiträge
// behalten ihre bisherige category-Zeichenkette, tauchen aber nicht mehr im
// Dropdown/den sortierbaren Filtern auf (bewusst kein Massen-Umhängen auf
// eine andere Kategorie, das wäre ein zu großer stiller Eingriff).
export async function deletePortfolioCategoryAction(categoryId: string) {
  if (!(await hasPermission("portfolio_delete"))) return;
  await connectDB();
  await PortfolioCategory.findByIdAndDelete(categoryId);
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
}

export async function movePortfolioCategoryAction(categoryId: string, direction: "up" | "down") {
  if (!(await hasPermission("portfolio_edit"))) return;
  await connectDB();
  const item = await PortfolioCategory.findById(categoryId).lean();
  if (!item) return;

  const neighbor = await PortfolioCategory.findOne(
    direction === "up" ? { sortOrder: { $lt: item.sortOrder } } : { sortOrder: { $gt: item.sortOrder } }
  )
    .sort({ sortOrder: direction === "up" ? -1 : 1 })
    .lean();
  if (!neighbor) return;

  await Promise.all([
    PortfolioCategory.findByIdAndUpdate(item._id, { sortOrder: neighbor.sortOrder }),
    PortfolioCategory.findByIdAndUpdate(neighbor._id, { sortOrder: item.sortOrder }),
  ]);
  revalidatePath(PORTFOLIO_ADMIN_PATH);
  revalidatePath("/de/portfolio");
}
