/**
 * Requisition service helpers shared by the crewing server actions and by the
 * system auto-raise paths (sign-off / end-of-contract / leave-clash backfill,
 * Phase 3/4). Kept out of the "use server" action module so non-action code can
 * import the auto-raise helper. See Crewing-Implementation-Spec §5.2/§5.3 (R6).
 */

import { db } from "@/lib/db";
import { generateRequisitionCode } from "@/lib/requisition-number";
import { notifyCrew } from "@/lib/notifier";
import type { Prisma, RequisitionReason, User } from "@prisma/client";

type Tx = Prisma.TransactionClient;

export interface NewRequisitionInput {
  rankId: string;
  vesselId?: string | null;
  siteId?: string | null;
  reason: RequisitionReason;
  neededBy?: Date | null;
  notes?: string | null;
  raisedById?: string | null; // null = system-raised
  autoRaised?: boolean;
}

type RequisitionWithRefs = Prisma.RequisitionGetPayload<{
  include: { rank: true; vessel: true; site: true };
}>;

/**
 * Core requisition creator — run inside a transaction. Generates the code and
 * writes the REQUISITION_RAISED CrewAction. Callers own notification + any
 * relief-request linking afterwards.
 */
export async function createRequisitionTx(
  tx: Tx,
  input: NewRequisitionInput
): Promise<RequisitionWithRefs> {
  const code = await generateRequisitionCode(tx);
  return tx.requisition.create({
    data: {
      code,
      reason: input.reason,
      autoRaised: input.autoRaised ?? false,
      neededBy: input.neededBy ?? null,
      notes: input.notes ?? null,
      rankId: input.rankId,
      vesselId: input.vesselId ?? null,
      siteId: input.siteId ?? null,
      raisedById: input.raisedById ?? null,
      actions: {
        create: {
          actionType: "REQUISITION_RAISED",
          actorId: input.raisedById ?? null,
          metadata: input.autoRaised ? { auto: true, reason: input.reason } : undefined,
        },
      },
    },
    include: { rank: true, vessel: true, site: true },
  });
}

/** Human label for a requisition's cost axis (vessel preferred, else site). */
export function requisitionLocationLabel(r: {
  vessel: { name: string } | null;
  site: { name: string } | null;
}): string {
  return r.vessel?.name ?? r.site?.name ?? "—";
}

/** Office recipients (MPO sources recruitment; Manager oversees). */
export function getOfficeRecipients(): Promise<User[]> {
  return db.user.findMany({
    where: { isActive: true, role: { in: ["MANNING", "MANAGER", "SUPERUSER"] } },
  });
}

/** MPO recipients — for "requisition raised → MPO" (spec §11). */
export function getMpoRecipients(): Promise<User[]> {
  return db.user.findMany({
    where: { isActive: true, role: { in: ["MANNING", "SUPERUSER"] } },
  });
}

/** Manager recipients — for the approval gates (salary / selection / waiver). */
export function getManagerRecipients(): Promise<User[]> {
  return db.user.findMany({
    where: { isActive: true, role: { in: ["MANAGER", "SUPERUSER"] } },
  });
}

/** Notify the office that a requisition was auto-raised. Call AFTER the
 * creating transaction commits (notifications are not part of the atomic write). */
export async function notifyAutoRaised(requisition: RequisitionWithRefs): Promise<void> {
  const recipients = await getOfficeRecipients();
  const loc = requisitionLocationLabel(requisition);
  await notifyCrew({
    event: "REQUISITION_RAISED",
    recipients,
    subject: `Requisition ${requisition.code} auto-raised`,
    body: `A ${requisition.rank.name} vacancy on ${loc} was auto-raised (${requisition.code}) — reason: ${requisition.reason}.`,
    link: `/crewing/requisitions/${requisition.id}`,
  });
}

/**
 * System auto-raise: an OPEN requisition with no human actor (autoRaised).
 * Sign-off, end-of-contract and the leave-clash detector funnel through here.
 * See spec §5.2/§5.3 (R6).
 *
 * Pass `tx` to create the backfill **atomically inside the caller's transaction**
 * (so an approved leave / sign-off can never commit without its backfill) — the
 * caller then owns the post-commit `notifyAutoRaised`. Called without `tx`, it
 * runs its own transaction and notifies itself.
 */
export async function autoRaiseRequisition(
  input: Omit<NewRequisitionInput, "raisedById" | "autoRaised">,
  tx?: Tx
): Promise<RequisitionWithRefs> {
  const data = { ...input, raisedById: null, autoRaised: true };
  if (tx) {
    // Caller's transaction — caller is responsible for notifyAutoRaised after commit.
    return createRequisitionTx(tx, data);
  }
  const requisition = await db.$transaction((t) => createRequisitionTx(t, data));
  await notifyAutoRaised(requisition);
  return requisition;
}
