"use server";

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { hasPermission } from "@/lib/permissions";
import { CREWING_ENABLED } from "@/lib/feature-flags";
import { RankCategory, SeafarerDocType } from "@prisma/client";
import { z } from "zod";
import { revalidatePath } from "next/cache";

type ActionResult = { ok: true } | { error: string };

async function guard(): Promise<{ error: string } | null> {
  if (!CREWING_ENABLED) return { error: "Crewing is not enabled" };
  const session = await auth();
  if (!session?.user || !hasPermission(session.user.role, "manage_ranks")) {
    return { error: "Unauthorized" };
  }
  return null;
}

const rankSchema = z.object({
  code: z.string().trim().min(1, "Code is required").max(16, "Code is too long"),
  name: z.string().trim().min(1, "Name is required"),
  description: z.string().optional(),
  parentId: z.string().optional(),
  category: z.nativeEnum(RankCategory),
  isSeafarer: z.boolean(),
  grantsLogin: z.boolean(),
});

function parseRank(formData: FormData) {
  return rankSchema.safeParse({
    code: formData.get("code"),
    name: formData.get("name"),
    description: (formData.get("description") as string) || undefined,
    parentId: (formData.get("parentId") as string) || undefined,
    category: formData.get("category"),
    isSeafarer: formData.get("isSeafarer") === "on" || formData.get("isSeafarer") === "true",
    grantsLogin: formData.get("grantsLogin") === "on" || formData.get("grantsLogin") === "true",
  });
}

// True if `candidateParentId` is `rankId` itself or one of its descendants —
// setting it as the parent would create a cycle.
async function wouldCycle(rankId: string, candidateParentId: string): Promise<boolean> {
  if (rankId === candidateParentId) return true;
  const all = await db.rank.findMany({ select: { id: true, parentId: true } });
  const childrenOf = new Map<string, string[]>();
  for (const r of all) {
    if (r.parentId) {
      const list = childrenOf.get(r.parentId) ?? [];
      list.push(r.id);
      childrenOf.set(r.parentId, list);
    }
  }
  const stack = [rankId];
  while (stack.length) {
    const cur = stack.pop()!;
    if (cur === candidateParentId) return true;
    stack.push(...(childrenOf.get(cur) ?? []));
  }
  return false;
}

export async function createRank(formData: FormData): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  const parsed = parseRank(formData);
  if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
  const data = parsed.data;

  const exists = await db.rank.findUnique({ where: { code: data.code } });
  if (exists) return { error: "A rank with that code already exists" };

  await db.rank.create({
    data: {
      code: data.code,
      name: data.name,
      description: data.description ?? null,
      parentId: data.parentId ?? null,
      category: data.category,
      isSeafarer: data.isSeafarer,
      grantsLogin: data.grantsLogin,
    },
  });
  revalidatePath("/admin/ranks");
  return { ok: true };
}

export async function updateRank(formData: FormData): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  const id = formData.get("id") as string;
  if (!id) return { error: "Rank ID is required" };

  const parsed = parseRank(formData);
  if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
  const data = parsed.data;

  const conflict = await db.rank.findFirst({ where: { code: data.code, id: { not: id } } });
  if (conflict) return { error: "Another rank already uses that code" };

  if (data.parentId && (await wouldCycle(id, data.parentId))) {
    return { error: "A rank cannot report to itself or one of its sub-ranks" };
  }

  await db.rank.update({
    where: { id },
    data: {
      code: data.code,
      name: data.name,
      description: data.description ?? null,
      parentId: data.parentId ?? null,
      category: data.category,
      isSeafarer: data.isSeafarer,
      grantsLogin: data.grantsLogin,
    },
  });
  revalidatePath("/admin/ranks");
  return { ok: true };
}

export async function deleteRank(id: string): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  const hasChildren = await db.rank.findFirst({ where: { parentId: id } });
  if (hasChildren) return { error: "Cannot delete: this rank has sub-ranks. Reassign or remove them first." };

  // Document requirements cascade on delete.
  await db.rank.delete({ where: { id } });
  revalidatePath("/admin/ranks");
  return { ok: true };
}

export async function toggleRankActive(id: string): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  const rank = await db.rank.findUnique({ where: { id }, select: { isActive: true } });
  if (!rank) return { error: "Rank not found" };

  await db.rank.update({ where: { id }, data: { isActive: !rank.isActive } });
  revalidatePath("/admin/ranks");
  return { ok: true };
}

const docReqSchema = z.object({
  rankId: z.string().min(1),
  docType: z.nativeEnum(SeafarerDocType),
  isMandatory: z.boolean(),
  note: z.string().optional(),
});

export async function addRankDocRequirement(formData: FormData): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  const parsed = docReqSchema.safeParse({
    rankId: formData.get("rankId"),
    docType: formData.get("docType"),
    isMandatory: formData.get("isMandatory") === "on" || formData.get("isMandatory") === "true",
    note: (formData.get("note") as string) || undefined,
  });
  if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };
  const data = parsed.data;

  await db.rankDocRequirement.upsert({
    where: { rankId_docType: { rankId: data.rankId, docType: data.docType } },
    update: { isMandatory: data.isMandatory, note: data.note ?? null },
    create: { rankId: data.rankId, docType: data.docType, isMandatory: data.isMandatory, note: data.note ?? null },
  });
  revalidatePath("/admin/ranks");
  return { ok: true };
}

export async function removeRankDocRequirement(id: string): Promise<ActionResult> {
  const denied = await guard();
  if (denied) return denied;

  await db.rankDocRequirement.delete({ where: { id } });
  revalidatePath("/admin/ranks");
  return { ok: true };
}
