"use server";

import { redirect } from "next/navigation";
import { Prisma, UserRole } from "@prisma/client";
import { z } from "zod";

import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";

// =====================================================================
// /onboarding · Server Action
// ---------------------------------------------------------------------
// Erstellt die Kita (Tenant) und verknüpft den eingeloggten Gründer in
// einer einzigen Datenbank-Transaktion. Damit ist garantiert: entweder
// existiert hinterher beides (Kita + verknüpfter Admin) oder gar nichts
// — kein "halb-eingerichteter" Zwischenzustand.
// =====================================================================

const onboardingSchema = z
  .object({
    kitaName: z
      .string()
      .trim()
      .min(2, "Bitte mindestens 2 Zeichen angeben.")
      .max(120, "Maximal 120 Zeichen."),
    notdienstModuleEnabled: z.preprocess(checkboxToBool, z.boolean()),
    terminModuleEnabled: z.preprocess(checkboxToBool, z.boolean()),
    adressbuchModuleEnabled: z.preprocess(checkboxToBool, z.boolean()),
    notdienstMinPerChildPerMonth: z.coerce
      .number()
      .int("Bitte eine ganze Zahl angeben.")
      .min(0, "Bitte einen Wert von 0 oder mehr angeben.")
      .max(31, "Maximal 31 Tage pro Monat."),
  })
  .superRefine((data, ctx) => {
    // Mindest-Verfügbarkeit nur relevant, wenn das Notdienst-Modul aktiv ist.
    // Bei aktivem Modul muss mindestens 1 Tag eingetragen sein — 0 wäre
    // fachlich sinnlos und würde den Planer leerlaufen lassen.
    if (data.notdienstModuleEnabled && data.notdienstMinPerChildPerMonth < 1) {
      ctx.addIssue({
        path: ["notdienstMinPerChildPerMonth"],
        code: "custom",
        message:
          "Bei aktivem Notdienst-Modul: mindestens 1 Tag pro Monat angeben.",
      });
    }
  });

function checkboxToBool(value: unknown): boolean {
  // HTML-Checkbox: "on" wenn aktiviert, sonst nicht im FormData.
  return value === "on" || value === "true" || value === true;
}

export type OnboardingState = {
  // Bumped after every action call so the client can key uncontrolled inputs
  // and force a remount with the echoed values from a failed submit.
  attempt: number;
  // Echoed inputs so a failed submit doesn't wipe the form.
  values?: {
    kitaName?: string;
    notdienstModuleEnabled?: boolean;
    terminModuleEnabled?: boolean;
    adressbuchModuleEnabled?: boolean;
    notdienstMinPerChildPerMonth?: number;
  };
  errors?: {
    kitaName?: string[];
    notdienstMinPerChildPerMonth?: string[];
    _form?: string[];
  };
};

function echoValues(formData: FormData): OnboardingState["values"] {
  const min = formData.get("notdienstMinPerChildPerMonth");
  return {
    kitaName: String(formData.get("kitaName") ?? ""),
    notdienstModuleEnabled: formData.get("notdienstModuleEnabled") === "on",
    terminModuleEnabled: formData.get("terminModuleEnabled") === "on",
    adressbuchModuleEnabled: formData.get("adressbuchModuleEnabled") === "on",
    notdienstMinPerChildPerMonth:
      min === null || min === "" ? undefined : Number(min),
  };
}

export async function completeOnboardingAction(
  prev: OnboardingState,
  formData: FormData,
): Promise<OnboardingState> {
  const attempt = prev.attempt + 1;
  const session = await auth();
  if (!session?.user) {
    redirect("/login");
  }
  if (session.user.kitaId) {
    // Onboarding bereits abgeschlossen — verhindert doppeltes Einrichten
    // (z.B. durch zurück-navigieren oder Replay des Formulars).
    redirect("/dashboard");
  }

  const parsed = onboardingSchema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) {
    return {
      attempt,
      values: echoValues(formData),
      errors: parsed.error.flatten().fieldErrors,
    };
  }
  const data = parsed.data;

  try {
    await prisma.$transaction(async (tx) => {
      const kita = await tx.kita.create({
        data: {
          name: data.kitaName,
          slug: await generateUniqueSlug(tx, data.kitaName),
          notdienstModuleEnabled: data.notdienstModuleEnabled,
          terminModuleEnabled: data.terminModuleEnabled,
          adressbuchModuleEnabled: data.adressbuchModuleEnabled,
          notdienstMinPerChildPerMonth: data.notdienstMinPerChildPerMonth,
        },
      });

      // Defense-in-Depth: erlaube den Update nur, wenn der User aktuell
      // tatsächlich noch keine Kita hat (verhindert Race-Conditions bei
      // parallelen Onboarding-Submits).
      const updated = await tx.user.updateMany({
        where: { id: session.user.id, kitaId: null },
        data: {
          kitaId: kita.id,
          role: UserRole.ADMIN,
        },
      });

      if (updated.count === 0) {
        throw new Error("USER_ALREADY_HAS_KITA");
      }
    });
  } catch (err) {
    if (err instanceof Error && err.message === "USER_ALREADY_HAS_KITA") {
      redirect("/dashboard");
    }
    if (err instanceof Prisma.PrismaClientKnownRequestError) {
      return {
        attempt,
        values: echoValues(formData),
        errors: { _form: ["Datenbankfehler — bitte erneut versuchen."] },
      };
    }
    throw err;
  }

  // Frischer JWT wird beim nächsten Request automatisch neu befüllt
  // (siehe jwt-Callback in auth.ts → re-validates from DB).
  redirect("/dashboard");
}

// ---------------------------------------------------------------------
// Helfer
// ---------------------------------------------------------------------

async function generateUniqueSlug(
  tx: Prisma.TransactionClient,
  name: string,
): Promise<string> {
  const base = slugify(name) || "kita";
  let candidate = base;
  let attempt = 0;
  // In der Praxis genügen 1-2 Iterationen; max. 50 als Safety-Net.
  while (attempt < 50) {
    const exists = await tx.kita.findUnique({ where: { slug: candidate } });
    if (!exists) return candidate;
    attempt += 1;
    candidate = `${base}-${attempt + 1}`;
  }
  return `${base}-${Date.now()}`;
}

function slugify(input: string): string {
  return input
    .toLowerCase()
    .replace(/ß/g, "ss")
    .normalize("NFKD")
    // strippt kombinierende Diakritika (ä → a, ö → o, é → e …)
    .replace(/[̀-ͯ]/g, "")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)/g, "")
    .slice(0, 60);
}
