/**
 * Integration tests for Crewing Phase 5a verification: documents (MPO) and
 * bank/EPF (Accounts), with role gating per §6/§8.11.
 */
import { vi, describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";

vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("@/lib/feature-flags", () => ({ CREWING_ENABLED: true, INVENTORY_ENABLED: true }));

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { verifyDocument, verifyBankEpf, recordEpfoCheck } from "@/app/(portal)/crewing/verification/actions";
import { makeSession, getSeedUser } from "./helpers";
import type { Role } from "@prisma/client";

let manningId: string;
let accountsId: string;
let siteStaffId: string;

const SS_EMAIL = "sitestaff@itver.local";
const as = (userId: string, role: Role) =>
  vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(userId, role));

async function crewWithRecords() {
  const c = await db.crewMember.create({ data: { name: "To Verify", status: "EMPLOYEE", type: "NEW", source: "CAREERS" } });
  const doc = await db.seafarerDocument.create({ data: { crewMemberId: c.id, docType: "PASSPORT", number: "P999" } });
  await db.bankDetail.create({ data: { crewMemberId: c.id, accountNumber: "123456789", ifsc: "HDFC0001" } });
  await db.epfDetail.create({ data: { crewMemberId: c.id, uan: "UAN-1" } });
  return { crewId: c.id, docId: doc.id };
}

beforeAll(async () => {
  manningId = (await getSeedUser("manning@pelagia.local")).id;
  accountsId = (await getSeedUser("accounts@pelagia.local")).id;
  const ss = await db.user.upsert({ where: { email: SS_EMAIL }, update: { role: "SITE_STAFF", isActive: true }, create: { employeeId: "ITVER-SS", email: SS_EMAIL, name: "SS Ver", role: "SITE_STAFF" } });
  siteStaffId = ss.id;
});

afterEach(async () => {
  await db.crewAction.deleteMany({});
  await db.seafarerDocument.deleteMany({});
  await db.bankDetail.deleteMany({});
  await db.epfDetail.deleteMany({});
  await db.crewMember.deleteMany({});
  vi.clearAllMocks();
});

afterAll(async () => {
  await db.user.deleteMany({ where: { email: SS_EMAIL } });
});

describe("document verification (MPO)", () => {
  it("verifies a document with an audit row", async () => {
    const { crewId, docId } = await crewWithRecords();
    as(manningId, "MANNING");
    expect("ok" in (await verifyDocument(docId, true))).toBe(true);
    const d = await db.seafarerDocument.findUniqueOrThrow({ where: { id: docId } });
    expect(d.verificationStatus).toBe("VERIFIED");
    expect(d.verifiedById).toBe(manningId);
    expect(await db.crewAction.count({ where: { crewMemberId: crewId, actionType: "RECORD_VERIFIED" } })).toBe(1);
  });

  it("rejection requires a reason and records it", async () => {
    const { docId } = await crewWithRecords();
    as(manningId, "MANNING");
    expect("error" in (await verifyDocument(docId, false))).toBe(true);
    expect("ok" in (await verifyDocument(docId, false, "Illegible scan"))).toBe(true);
    expect((await db.seafarerDocument.findUniqueOrThrow({ where: { id: docId } })).verificationStatus).toBe("REJECTED");
  });

  it("won't re-verify an already-decided document", async () => {
    const { docId } = await crewWithRecords();
    as(manningId, "MANNING");
    await verifyDocument(docId, true);
    expect("error" in (await verifyDocument(docId, true))).toBe(true);
  });

  it("is rejected for roles without verify_site_records (accounts, site staff)", async () => {
    const { docId } = await crewWithRecords();
    as(accountsId, "ACCOUNTS");
    expect(await verifyDocument(docId, true)).toEqual({ error: "Unauthorized" });
    as(siteStaffId, "SITE_STAFF");
    expect(await verifyDocument(docId, true)).toEqual({ error: "Unauthorized" });
  });
});

describe("bank/EPF verification (Accounts)", () => {
  it("Accounts verifies bank and EPF", async () => {
    const { crewId } = await crewWithRecords();
    as(accountsId, "ACCOUNTS");
    expect("ok" in (await verifyBankEpf(crewId, "bank", true))).toBe(true);
    expect((await db.bankDetail.findUniqueOrThrow({ where: { crewMemberId: crewId } })).verificationStatus).toBe("VERIFIED");
    expect("ok" in (await verifyBankEpf(crewId, "epf", true))).toBe(true);
    expect((await db.epfDetail.findUniqueOrThrow({ where: { crewMemberId: crewId } })).verificationStatus).toBe("VERIFIED");
  });

  it("is rejected for the MPO (no verify_bank_epf)", async () => {
    const { crewId } = await crewWithRecords();
    as(manningId, "MANNING");
    expect(await verifyBankEpf(crewId, "bank", true)).toEqual({ error: "Unauthorized" });
  });
});

describe("EPFO assisted check (recordEpfoCheck)", () => {
  it("records the EPFO member name + timestamp (Accounts)", async () => {
    const { crewId } = await crewWithRecords();
    as(accountsId, "ACCOUNTS");
    expect("ok" in (await recordEpfoCheck(crewId, "EPFO Member (stub)"))).toBe(true);
    const epf = await db.epfDetail.findUniqueOrThrow({ where: { crewMemberId: crewId } });
    expect(epf.epfoMemberName).toBe("EPFO Member (stub)");
    expect(epf.epfoCheckedAt).not.toBeNull();
  });

  it("is rejected for the MPO (no verify_bank_epf)", async () => {
    const { crewId } = await crewWithRecords();
    as(manningId, "MANNING");
    expect(await recordEpfoCheck(crewId, "x")).toEqual({ error: "Unauthorized" });
  });
});
