/**
 * Integration tests for PO creation server action.
 * Covers: S-01 (create with line items), S-02 (save as draft), S-03 (submit for approval).
 */
import { vi, describe, it, expect, beforeAll, afterEach } from "vitest";

vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("@/lib/notifier", () => ({ notify: vi.fn() }));

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { createPo } from "@/app/(portal)/po/new/actions";
import {
  makeSession, getSeedUser, getSeedVessel, getSeedAccount, getSeedVendor,
  makePoForm, deletePosByTitle,
} from "./helpers";

const PREFIX = "INTTEST_CREATE_";
let techId: string;
let vesselId: string;
let accountId: string;
let vendorId: string;

beforeAll(async () => {
  const [tech, vessel, account, vendor] = await Promise.all([
    getSeedUser("tech@pelagia.local"),
    getSeedVessel("MV Aegean Wind"),
    getSeedAccount("700201"),
    getSeedVendor("Apar Industries Ltd"),
  ]);
  techId = tech.id;
  vesselId = vessel.id;
  accountId = account.id;
  vendorId = vendor.id;
});

afterEach(async () => {
  await deletePosByTitle(PREFIX);
});

// ── S-02: Save as draft ──────────────────────────────────────────────────────

describe("S-02 — save as draft", () => {
  it("creates a PO in DRAFT status", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    const form = makePoForm({
      title: `${PREFIX}Draft`,
      vesselId, accountId, intent: "draft",
    });
    const result = await createPo(form);

    expect(result).not.toHaveProperty("error");
    const id = (result as { id: string }).id;
    const po = await db.purchaseOrder.findUnique({ where: { id } });
    expect(po?.status).toBe("DRAFT");
    expect(po?.submittedAt).toBeNull();
  });

  it("returns error for unauthenticated request", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(null);
    const form = makePoForm({ title: `${PREFIX}Unauth`, vesselId, accountId });
    const result = await createPo(form);
    expect(result).toEqual({ error: "Unauthorized" });
  });

  it("returns error when ACCOUNTS role tries to create a PO", async () => {
    const acct = await getSeedUser("accounts@pelagia.local");
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(acct.id, "ACCOUNTS"));
    const form = makePoForm({ title: `${PREFIX}ForbiddenAccts`, vesselId, accountId });
    const result = await createPo(form);
    expect(result).toHaveProperty("error");
  });

  it("returns error when a required field (vesselId) is missing", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));
    const form = new FormData();
    form.set("title", `${PREFIX}NoVessel`);
    form.set("accountId", accountId);
    form.set("intent", "draft");
    form.set("lineItems[0].name", "Item");
    form.set("lineItems[0].quantity", "1");
    form.set("lineItems[0].unit", "pc");
    form.set("lineItems[0].unitPrice", "50");
    form.set("lineItems[0].gstRate", "0.18");
    const result = await createPo(form);
    expect(result).toHaveProperty("error");
  });
});

// ── S-01: Create with line items ─────────────────────────────────────────────

describe("S-01 — create PO with line items", () => {
  it("stores line items with correct quantity, unit price, and GST rate", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    const form = makePoForm({
      title: `${PREFIX}LineItems`,
      vesselId, accountId, vendorId, intent: "draft",
      lineItems: [
        { description: "Gear Oil 80W90", quantity: 50, unit: "L", unitPrice: 182, gstRate: 0.18 },
        { description: "Engine Filter",   quantity: 4,  unit: "pc", unitPrice: 250, gstRate: 0.12 },
      ],
    });
    const result = await createPo(form);
    const id = (result as { id: string }).id;

    const po = await db.purchaseOrder.findUnique({
      where: { id },
      include: { lineItems: { orderBy: { sortOrder: "asc" } } },
    });

    expect(po?.lineItems).toHaveLength(2);
    expect(Number(po!.lineItems[0].quantity)).toBe(50);
    expect(Number(po!.lineItems[0].unitPrice)).toBe(182);
    expect(Number(po!.lineItems[0].gstRate)).toBeCloseTo(0.18);
    expect(Number(po!.lineItems[1].unitPrice)).toBe(250);
    expect(Number(po!.lineItems[1].gstRate)).toBeCloseTo(0.12);
  });

  it("sets totalAmount to grand total including GST", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    // 10 × 100 × 1.18 = 1180
    const form = makePoForm({
      title: `${PREFIX}GrandTotal`,
      vesselId, accountId, intent: "draft",
      lineItems: [{ description: "Item", quantity: 10, unit: "pc", unitPrice: 100, gstRate: 0.18 }],
    });
    const result = await createPo(form);
    const id = (result as { id: string }).id;
    const po = await db.purchaseOrder.findUnique({ where: { id } });
    expect(Number(po!.totalAmount)).toBeCloseTo(1180, 1);
  });

  it("stores optional fields (PI quotation no, place of delivery, TC fields)", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    const form = makePoForm({ title: `${PREFIX}Optional`, vesselId, accountId, intent: "draft" });
    form.set("piQuotationNo", "Verbal");
    form.set("placeOfDelivery", "CBD Belapur, Navi Mumbai");
    form.set("tcDelivery", "Within 7 days");
    form.set("tcPaymentTerms", "Net 45");
    const result = await createPo(form);
    const id = (result as { id: string }).id;
    const po = await db.purchaseOrder.findUnique({ where: { id } });

    expect((po as any).piQuotationNo).toBe("Verbal");
    expect((po as any).placeOfDelivery).toBe("CBD Belapur, Navi Mumbai");
    expect((po as any).tcDelivery).toBe("Within 7 days");
    expect((po as any).tcPaymentTerms).toBe("Net 45");
  });

  it("allows MANNING role to create a PO", async () => {
    const manning = await getSeedUser("manning@pelagia.local");
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(manning.id, "MANNING"));
    const form = makePoForm({ title: `${PREFIX}Manning`, vesselId, accountId });
    const result = await createPo(form);
    expect(result).not.toHaveProperty("error");
  });
});

// ── S-03: Submit for approval ─────────────────────────────────────────────────

describe("S-03 — submit for approval", () => {
  it("creates PO with status MGR_REVIEW and sets submittedAt", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    const form = makePoForm({ title: `${PREFIX}Submit`, vesselId, accountId, intent: "submit" });
    const result = await createPo(form);

    expect(result).not.toHaveProperty("error");
    const id = (result as { id: string }).id;
    const po = await db.purchaseOrder.findUnique({ where: { id } });
    expect(po?.status).toBe("MGR_REVIEW");
    expect(po?.submittedAt).not.toBeNull();
  });

  it("sends notification to managers on submit", async () => {
    const { notify } = await import("@/lib/notifier");
    vi.mocked(notify).mockClear();
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));

    const form = makePoForm({ title: `${PREFIX}Notify`, vesselId, accountId, intent: "submit" });
    await createPo(form);

    expect(vi.mocked(notify)).toHaveBeenCalledWith(
      expect.objectContaining({ event: "PO_SUBMITTED" })
    );
  });
});
