// src/lib/admin/staff-actions.ts — gestion des opérateurs Staff SYS (spec §7).
// Gate : staff:manage (jamais délégable ⇒ Admin SYS de fait).
"use server";
import { APIError } from "better-auth/api";
import { revalidatePath } from "next/cache";
import { headers } from "next/headers";
import { z } from "zod";
import { auth } from "@/lib/auth";
import {
  assertSysPermission,
  getSysOperatorSession,
} from "@/lib/auth/sys-permissions";
import { db } from "@/lib/db/client";
import { recordAdminAction } from "./audit";
import {
  listStaff,
  promoteToStaff,
  removeStaff,
  type StaffRow,
  setStaffPermissions,
} from "./staff-management/core";

const emailSchema = z.string().trim().toLowerCase().pipe(z.email());
const createStaffSchema = z.object({
  name: z.string().trim().min(1).max(120),
  email: emailSchema,
  password: z.string().min(12).max(128),
});

export async function listStaffAction(): Promise<StaffRow[]> {
  await assertSysPermission("staff:manage");
  return listStaff(db);
}

/** Crée un compte Staff SYS (voie normale — jamais le signup public).
 *  auth.api.createUser exige un admin Better Auth : l'Admin SYS a role:"admin"
 *  (bridge SUPER_ADMIN_EMAILS), donc l'appel passe. Le hook user.create.after
 *  saute le bootstrap d'org pour role "staff" (Task 6). */
export async function createStaffAction(input: {
  name: string;
  email: string;
  password: string;
}): Promise<{ ok: true } | { ok: false; error: string }> {
  await assertSysPermission("staff:manage");
  const parsed = createStaffSchema.safeParse(input);
  if (!parsed.success)
    return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalide" };
  const { name, email, password } = parsed.data;
  try {
    await auth.api.createUser({
      headers: await headers(),
      body: {
        name,
        email,
        password,
        // Le type $Infer de createUser ne connaît que defaultRole/adminRoles
        // ("user" | "admin" — config adminPlugin dans auth.ts) : "staff" est
        // un rôle système hors admin plugin, absent de cette union TS. Le
        // schéma runtime, lui, accepte n'importe quelle string (z.string()
        // côté Better Auth) — seul le typage est trop étroit ; cast minimal.
        role: "staff" as "user" | "admin",
      },
    });
  } catch (e) {
    return {
      ok: false,
      error:
        e instanceof APIError
          ? e.message
          : "Création impossible (email déjà utilisé ?)",
    };
  }
  await recordAdminAction({
    action: "staff.create",
    targetType: "user",
    targetLabel: email,
    metadata: { name },
  });
  revalidatePath("/admin/staff");
  return { ok: true };
}

export async function promoteToStaffAction(
  email: string,
): Promise<{ ok: true } | { ok: false; error: string }> {
  await assertSysPermission("staff:manage");
  const op = await getSysOperatorSession();
  const res = await promoteToStaff(
    db,
    op?.userId ?? "",
    emailSchema.parse(email),
  );
  if (res.ok) {
    await recordAdminAction({
      action: "staff.promote",
      targetType: "user",
      targetId: res.userId,
      targetLabel: email,
    });
    revalidatePath("/admin/staff");
    return { ok: true };
  }
  return res;
}

export async function setStaffPermissionsAction(
  userId: string,
  perms: string[],
): Promise<{ ok: true } | { ok: false; error: string }> {
  await assertSysPermission("staff:manage");
  const op = await getSysOperatorSession();
  const res = await setStaffPermissions(db, op?.userId ?? "", userId, perms);
  if (res.ok) {
    await recordAdminAction({
      action: "staff.set_permissions",
      targetType: "user",
      targetId: userId,
      metadata: { permissions: [...perms].sort() },
    });
    revalidatePath("/admin/staff");
  }
  return res;
}

export async function removeStaffAction(
  userId: string,
): Promise<{ ok: true } | { ok: false; error: string }> {
  await assertSysPermission("staff:manage");
  const op = await getSysOperatorSession();
  const res = await removeStaff(db, op?.userId ?? "", userId);
  if (res.ok) {
    await recordAdminAction({
      action: "staff.remove",
      targetType: "user",
      targetId: userId,
    });
    revalidatePath("/admin/staff");
  }
  return res;
}
