"use server";

import { revalidatePath } from "next/cache";
import { prisma } from "@/lib/prisma";
import { requireSession } from "@/lib/auth/session";
import { isItemKind, templateFor, type ItemKind } from "./templates";
import type { ProtocolItemKind } from "@prisma/client";

// ──────────────────────────────────────────────────────────────────────────
// Schreibzugriffe des Protokoll-Moduls.
// `changelog.manage` darf Protokolle und Einträge pflegen; löschen darf ein
// Protokoll nur, wer es angelegt hat — oder die Administration.
// ──────────────────────────────────────────────────────────────────────────

const MAX_ITEMS = 60;
const MAX_ITEM_TEXT = 500;

export type ProtocolResult = { ok?: true; id?: string; error?: string };

function clean(v: string | null | undefined, max: number): string | null {
  const s = (v ?? "").trim();
  return s ? s.slice(0, max) : null;
}

function safeColor(v: string | null | undefined): string | null {
  if (!v) return null;
  return /^#[0-9a-fA-F]{3,8}$/.test(v) ? v.slice(0, 16) : null;
}

async function guard(): Promise<
  { userId: string; isAdmin: boolean } | { error: string }
> {
  const session = await requireSession();
  if (!session.user.permissions?.includes("changelog.manage")) {
    return { error: "Keine Berechtigung, Protokolle zu pflegen." };
  }
  return {
    userId: session.user.id,
    isAdmin: Boolean(session.user.permissions?.includes("admin.access")),
  };
}

// ── Protokolle ────────────────────────────────────────────────────────────

export type ProtocolInput = {
  name: string;
  description?: string | null;
  color?: string | null;
  icon?: string | null;
};

export async function createProtocolAction(input: ProtocolInput): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  const name = clean(input.name, 160);
  if (!name) return { error: "Name darf nicht leer sein." };

  const count = await prisma.protocol.count();
  const created = await prisma.protocol.create({
    data: {
      name,
      description: clean(input.description, 500),
      color: safeColor(input.color),
      icon: clean(input.icon, 40),
      position: count,
      createdById: ctx.userId,
    },
    select: { id: true },
  });

  revalidatePath("/changelog");
  return { ok: true, id: created.id };
}

export async function updateProtocolAction(
  protocolId: string,
  input: ProtocolInput,
): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  const name = clean(input.name, 160);
  if (!name) return { error: "Name darf nicht leer sein." };

  await prisma.protocol.update({
    where: { id: protocolId },
    data: {
      name,
      description: clean(input.description, 500),
      color: safeColor(input.color),
      icon: clean(input.icon, 40),
    },
  });

  revalidatePath("/changelog");
  return { ok: true, id: protocolId };
}

export async function deleteProtocolAction(protocolId: string): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  const protocol = await prisma.protocol.findUnique({
    where: { id: protocolId },
    select: { createdById: true },
  });
  if (!protocol) return { error: "Protokoll nicht gefunden." };
  if (protocol.createdById !== ctx.userId && !ctx.isAdmin) {
    return { error: "Nur die Ersteller:in oder ein Admin darf das Protokoll löschen." };
  }

  // Einträge und Punkte gehen per Cascade mit.
  await prisma.protocol.delete({ where: { id: protocolId } });

  revalidatePath("/changelog");
  return { ok: true };
}

// ── Einträge ──────────────────────────────────────────────────────────────

export type EntryItemInput = { kind: string; text: string };

export type EntryInput = {
  protocolId: string;
  title: string;
  version?: string | null;
  /** ISO-Datum (YYYY-MM-DD). */
  date: string;
  template: string;
  summary?: string | null;
  publish: boolean;
  items: EntryItemInput[];
};

function parseDate(v: string): Date | null {
  const d = new Date(`${v}T12:00:00`);
  return Number.isNaN(d.getTime()) ? null : d;
}

/** Punkte säubern: leere Zeilen raus, Reihenfolge je Abschnitt merken. */
function normalizeItems(items: EntryItemInput[]) {
  const counters = new Map<ItemKind, number>();
  const out: { kind: ProtocolItemKind; text: string; position: number }[] = [];

  for (const raw of items.slice(0, MAX_ITEMS)) {
    const text = raw.text.trim();
    if (!text || !isItemKind(raw.kind)) continue;
    const kind = raw.kind;
    const position = counters.get(kind) ?? 0;
    counters.set(kind, position + 1);
    out.push({ kind: kind as ProtocolItemKind, text: text.slice(0, MAX_ITEM_TEXT), position });
  }
  return out;
}

export async function createEntryAction(input: EntryInput): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  const title = clean(input.title, 200);
  if (!title) return { error: "Titel darf nicht leer sein." };

  const date = parseDate(input.date);
  if (!date) return { error: "Bitte ein gültiges Datum wählen." };

  const protocol = await prisma.protocol.findUnique({
    where: { id: input.protocolId },
    select: { id: true },
  });
  if (!protocol) return { error: "Protokoll nicht gefunden." };

  const template = templateFor(input.template);
  const items = normalizeItems(input.items);

  const entry = await prisma.protocolEntry.create({
    data: {
      protocolId: protocol.id,
      title,
      version: template.withVersion ? clean(input.version, 40) : null,
      date,
      template: template.key,
      summary: clean(input.summary, 4000),
      status: input.publish ? "PUBLISHED" : "DRAFT",
      authorId: ctx.userId,
      items: { create: items },
    },
    select: { id: true },
  });

  revalidatePath("/changelog");
  return { ok: true, id: entry.id };
}

export async function updateEntryAction(
  entryId: string,
  input: EntryInput,
): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  const existing = await prisma.protocolEntry.findUnique({
    where: { id: entryId },
    select: { id: true },
  });
  if (!existing) return { error: "Eintrag nicht gefunden." };

  const title = clean(input.title, 200);
  if (!title) return { error: "Titel darf nicht leer sein." };

  const date = parseDate(input.date);
  if (!date) return { error: "Bitte ein gültiges Datum wählen." };

  const template = templateFor(input.template);
  const items = normalizeItems(input.items);

  // Punkte werden komplett ersetzt — einfacher und immer konsistent.
  await prisma.$transaction([
    prisma.protocolItem.deleteMany({ where: { entryId } }),
    prisma.protocolEntry.update({
      where: { id: entryId },
      data: {
        title,
        version: template.withVersion ? clean(input.version, 40) : null,
        date,
        template: template.key,
        summary: clean(input.summary, 4000),
        status: input.publish ? "PUBLISHED" : "DRAFT",
        items: { create: items },
      },
    }),
  ]);

  revalidatePath("/changelog");
  return { ok: true, id: entryId };
}

export async function deleteEntryAction(entryId: string): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  await prisma.protocolEntry.delete({ where: { id: entryId } });

  revalidatePath("/changelog");
  return { ok: true };
}

/** Entwurf veröffentlichen bzw. wieder zurückziehen. */
export async function setEntryStatusAction(
  entryId: string,
  publish: boolean,
): Promise<ProtocolResult> {
  const ctx = await guard();
  if ("error" in ctx) return { error: ctx.error };

  await prisma.protocolEntry.update({
    where: { id: entryId },
    data: { status: publish ? "PUBLISHED" : "DRAFT" },
  });

  revalidatePath("/changelog");
  return { ok: true, id: entryId };
}
