"use server";

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { hasPermission, type Permission } from "@/lib/permissions";
import { CREWING_ENABLED } from "@/lib/feature-flags";
import { canPerformAction, canReject } from "@/lib/appraisal-state-machine";
import { getManagerRecipients, getMpoRecipients } from "@/lib/requisition-service";
import { notifyCrew } from "@/lib/notifier";
import type { Role } from "@prisma/client";
import { z } from "zod";
import { revalidatePath } from "next/cache";

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

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

function loadAppraisal(id: string) {
  return db.appraisal.findUnique({
    where: { id },
    include: { assignment: { include: { crewMember: { select: { id: true, name: true } }, rank: { select: { name: true } } } } },
  });
}

function revalidate(crewMemberId: string) {
  revalidatePath(`/crewing/crew/${crewMemberId}`);
  revalidatePath("/crewing/verification");
  revalidatePath("/approvals");
}

// ── Raise an appraisal (PM / site staff) ───────────────────────────────────────

const raiseSchema = z.object({
  assignmentId: z.string().min(1, "Crew assignment is required"),
  period: z.string().trim().min(1, "Period is required"),
  comments: z.string().optional(),
  competence: z.coerce.number().int().min(1).max(5).optional(),
  conduct: z.coerce.number().int().min(1).max(5).optional(),
  safety: z.coerce.number().int().min(1).max(5).optional(),
});

export async function raiseAppraisal(formData: FormData): Promise<ActionResult> {
  const g = await guard("raise_appraisal");
  if ("error" in g) return g;

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

  const assignment = await db.crewAssignment.findUnique({
    where: { id: d.assignmentId },
    include: { crewMember: { select: { id: true, name: true } }, rank: { select: { name: true } } },
  });
  if (!assignment) return { error: "Crew assignment not found" };

  const appraisal = await db.appraisal.create({
    data: {
      assignmentId: d.assignmentId,
      period: d.period,
      comments: d.comments ?? null,
      ratings: { competence: d.competence ?? null, conduct: d.conduct ?? null, safety: d.safety ?? null },
      status: "SUBMITTED",
      addedById: g.userId,
    },
  });
  await db.crewAction.create({ data: { actionType: "APPRAISAL_SUBMITTED", actorId: g.userId, crewMemberId: assignment.crewMember.id } });

  const mpos = await getMpoRecipients();
  await notifyCrew({
    event: "APPRAISAL_FOR_VERIFICATION",
    recipients: mpos,
    subject: `Appraisal to verify — ${assignment.crewMember.name}`,
    body: `An appraisal for ${assignment.crewMember.name} (${assignment.rank.name}, ${d.period}) awaits MPO verification.`,
    link: "/crewing/verification",
  });

  revalidate(assignment.crewMember.id);
  return { ok: true, id: appraisal.id };
}

// ── Verify (MPO) ───────────────────────────────────────────────────────────────

export async function verifyAppraisal(id: string, approve: boolean, remarks?: string): Promise<ActionResult> {
  const g = await guard("verify_appraisal");
  if ("error" in g) return g;

  const a = await loadAppraisal(id);
  if (!a) return { error: "Appraisal not found" };

  if (!approve) {
    if (!canReject(a.status)) return { error: `Cannot reject from ${a.status}` };
    if (!remarks?.trim()) return { error: "A reason is required to reject" };
    await db.appraisal.update({ where: { id }, data: { status: "REJECTED", rejectedReason: remarks.trim() } });
    await db.crewAction.create({ data: { actionType: "APPRAISAL_REJECTED", actorId: g.userId, crewMemberId: a.assignment.crewMember.id, note: remarks.trim() } });
    revalidate(a.assignment.crewMember.id);
    return { ok: true };
  }

  if (!canPerformAction(a.status, "verify", g.role)) return { error: `Cannot verify from ${a.status}` };
  await db.appraisal.update({ where: { id }, data: { status: "MPO_VERIFIED", verifiedById: g.userId } });
  await db.crewAction.create({ data: { actionType: "APPRAISAL_VERIFIED", actorId: g.userId, crewMemberId: a.assignment.crewMember.id } });

  const managers = await getManagerRecipients();
  await notifyCrew({
    event: "APPRAISAL_FOR_APPROVAL",
    recipients: managers,
    subject: `Appraisal for approval — ${a.assignment.crewMember.name}`,
    body: `${a.assignment.crewMember.name}'s appraisal (${a.assignment.rank.name}, ${a.period}) has been MPO-verified and awaits your approval.`,
    link: "/approvals",
  });

  revalidate(a.assignment.crewMember.id);
  return { ok: true };
}

// ── Approve (Manager) ──────────────────────────────────────────────────────────

export async function approveAppraisal(id: string, approve: boolean, remarks?: string): Promise<ActionResult> {
  const g = await guard("approve_appraisal");
  if ("error" in g) return g;

  const a = await loadAppraisal(id);
  if (!a) return { error: "Appraisal not found" };

  if (!approve) {
    if (!canReject(a.status)) return { error: `Cannot return from ${a.status}` };
    if (!remarks?.trim()) return { error: "A reason is required to return" };
    await db.appraisal.update({ where: { id }, data: { status: "REJECTED", rejectedReason: remarks.trim() } });
    await db.crewAction.create({ data: { actionType: "APPRAISAL_REJECTED", actorId: g.userId, crewMemberId: a.assignment.crewMember.id, note: remarks.trim() } });
    revalidate(a.assignment.crewMember.id);
    return { ok: true };
  }

  if (!canPerformAction(a.status, "approve", g.role)) return { error: `Cannot approve from ${a.status}` };
  await db.appraisal.update({ where: { id }, data: { status: "MANAGER_APPROVED", approvedById: g.userId } });
  await db.crewAction.create({ data: { actionType: "APPRAISAL_APPROVED", actorId: g.userId, crewMemberId: a.assignment.crewMember.id } });

  revalidate(a.assignment.crewMember.id);
  return { ok: true };
}
