"use server";

import { hash } from "bcryptjs";
import { z } from "zod";

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

// =====================================================================
// /invite/[token] · Server Action
// ---------------------------------------------------------------------
// acceptInviteAction:
//   1. Token erneut gegen DB prüfen (kein Trust auf Client-State)
//   2. Passwort hashen + User-Daten setzen (DSGVO-Timestamps)
//   3. Token löschen (kein Replay möglich)
//   4. Direkt einloggen + Redirect auf /dashboard
// =====================================================================

const inviteSchema = z
  .object({
    token: z.string().min(1),
    password: z.string().min(8, "Mindestens 8 Zeichen.").max(72, "Maximal 72 Zeichen."),
    confirmPassword: z.string().min(1, "Passwort-Bestätigung fehlt."),
    acceptPrivacyPolicy: z.literal("on", {
      error: "Bitte die Datenschutzerklärung akzeptieren.",
    }),
    directoryOptIn: z.enum(["on", "off"]).optional(),
  })
  .refine((d) => d.password === d.confirmPassword, {
    message: "Passwörter stimmen nicht überein.",
    path: ["confirmPassword"],
  });

const PRIVACY_POLICY_VERSION = "2026-05-01";

export type InviteState = {
  // Bumped per call so the client can key uncontrolled inputs and force a
  // remount after a failed submit (React's <form action> wipes them
  // otherwise).
  attempt: number;
  // Echoed checkbox states so failed submits don't lose the user's consent
  // ticks. Passwords are NEVER echoed back from the server.
  values?: {
    acceptPrivacyPolicy?: boolean;
    directoryOptIn?: boolean;
  };
  errors?: {
    password?: string[];
    confirmPassword?: string[];
    acceptPrivacyPolicy?: string[];
    directoryOptIn?: string[];
    _form?: string[];
  };
};

function echoCheckboxValues(formData: FormData): InviteState["values"] {
  return {
    acceptPrivacyPolicy: formData.get("acceptPrivacyPolicy") === "on",
    directoryOptIn: formData.get("directoryOptIn") === "on",
  };
}

export async function acceptInviteAction(
  prev: InviteState,
  formData: FormData,
): Promise<InviteState> {
  const attempt = prev.attempt + 1;
  const values = echoCheckboxValues(formData);

  const parsed = inviteSchema.safeParse({
    token: formData.get("token"),
    password: formData.get("password"),
    confirmPassword: formData.get("confirmPassword"),
    acceptPrivacyPolicy: formData.get("acceptPrivacyPolicy"),
    directoryOptIn: formData.get("directoryOptIn") ?? "off",
  });

  if (!parsed.success) {
    return { attempt, values, errors: parsed.error.flatten().fieldErrors };
  }

  const { token, password, directoryOptIn } = parsed.data;
  const now = new Date();

  // ── 1. Token erneut DB-seitig validieren ──────────────────────────
  const verificationToken = await prisma.verificationToken.findUnique({
    where: { token },
  });

  if (!verificationToken) {
    return {
      attempt,
      values,
      errors: { _form: ["Einladungslink ist ungültig."] },
    };
  }
  if (verificationToken.expires < now) {
    return {
      attempt,
      values,
      errors: {
        _form: [
          "Dieser Einladungslink ist abgelaufen. Bitte wende dich an deinen Administrator.",
        ],
      },
    };
  }

  const userId = verificationToken.identifier;

  // ── 2. User validieren (existiert und hat noch kein Passwort) ──────
  const user = await prisma.user.findUnique({
    where: { id: userId },
    select: { id: true, email: true, passwordHash: true },
  });

  if (!user) {
    return {
      attempt,
      values,
      errors: { _form: ["Benutzer nicht gefunden."] },
    };
  }
  if (user.passwordHash !== "") {
    // Invite wurde bereits eingelöst
    return {
      attempt,
      values,
      errors: {
        _form: [
          "Dieser Einladungslink wurde bereits verwendet. Bitte melde dich an.",
        ],
      },
    };
  }

  // ── 3. Passwort hashen ─────────────────────────────────────────────
  const passwordHash = await hash(password, 12);

  // ── 4. User updaten + Token löschen (atomar) ──────────────────────
  await prisma.$transaction([
    prisma.user.update({
      where: { id: userId },
      data: {
        passwordHash,
        privacyPolicyAcceptedAt: now,
        privacyPolicyVersion: PRIVACY_POLICY_VERSION,
        directoryOptInAt: directoryOptIn === "on" ? now : null,
        emailVerifiedAt: now,
        lastLoginAt: now,
      },
    }),
    prisma.verificationToken.delete({
      where: { token },
    }),
  ]);

  // ── 5. Direkt einloggen → wirft NEXT_REDIRECT ─────────────────────
  await signIn("credentials", {
    email: user.email,
    password,
    redirectTo: "/dashboard",
  });

  // Unreachable – signIn redirected.
  return { attempt };
}
