/**
 * Integration tests for prepareVendorEmail (issue #14) — the "Email to vendor"
 * action that renders the PO PDF, stores it, and returns an Outlook mailto draft
 * with a download link. PdfService + storage are mocked (no Chromium / R2).
 */
import { vi, describe, it, expect, beforeAll, afterEach, afterAll } from "vitest";

vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("@/lib/pdf-service", () => ({
  renderPoPdf: vi.fn(async () => Buffer.from("%PDF-1.4 fake")),
  isPdfServiceConfigured: vi.fn(() => true),
  PdfServiceError: class PdfServiceError extends Error {},
}));
vi.mock("@/lib/storage", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/lib/storage")>();
  return {
    ...actual,
    uploadBuffer: vi.fn(async () => {}),
    generateDownloadUrl: vi.fn(async () => "https://files.example/po.pdf?sig=abc"),
  };
});

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { prepareVendorEmail } from "@/app/(portal)/po/[id]/email-actions";
import { isPdfServiceConfigured } from "@/lib/pdf-service";
import { makeSession, getSeedUser, getSeedVessel, getSeedAccount } from "./helpers";

const PREFIX = "INTTEST_EMAILVENDOR_";
let techId: string;
let vesselId: string;
let accountId: string;
let vendorWithEmailId: string;
let vendorNoEmailId: string;

const as = (userId: string, role: "TECHNICAL" | "MANAGER") =>
  vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(userId, role));

async function makePo(status: string, vendorId: string): Promise<string> {
  const po = await db.purchaseOrder.create({
    data: {
      poNumber: `${PREFIX}${status}-${Date.now()}-${Math.round(Math.random() * 1e6)}`,
      title: `${PREFIX}PO`,
      status: status as never,
      totalAmount: 1000,
      currency: "INR",
      vesselId,
      accountId,
      submitterId: techId,
      vendorId,
    },
  });
  return po.id;
}

beforeAll(async () => {
  const [tech, vessel, account] = await Promise.all([
    getSeedUser("tech@pelagia.local"),
    getSeedVessel("MV Poseidon"),
    getSeedAccount("700201"),
  ]);
  techId = tech.id;
  vesselId = vessel.id;
  accountId = account.id;

  const withEmail = await db.vendor.create({
    data: { name: `${PREFIX}WithEmail`, contacts: { create: { name: "Vinod", email: "vinod@vendor.test", isPrimary: true } } },
  });
  vendorWithEmailId = withEmail.id;
  const noEmail = await db.vendor.create({ data: { name: `${PREFIX}NoEmail` } });
  vendorNoEmailId = noEmail.id;
});

afterEach(() => {
  vi.mocked(isPdfServiceConfigured).mockReturnValue(true);
  vi.clearAllMocks();
});

afterAll(async () => {
  await db.purchaseOrder.deleteMany({ where: { title: { startsWith: PREFIX } } });
  await db.vendorContact.deleteMany({ where: { vendor: { name: { startsWith: PREFIX } } } });
  await db.vendor.deleteMany({ where: { name: { startsWith: PREFIX } } });
});

describe("prepareVendorEmail", () => {
  it("builds a mailto draft to the vendor's primary contact with the PDF link", async () => {
    as(techId, "TECHNICAL");
    const poId = await makePo("MGR_APPROVED", vendorWithEmailId);

    const result = await prepareVendorEmail(poId);
    expect("ok" in result && result.ok).toBe(true);
    if (!("ok" in result)) throw new Error(result.error);

    expect(result.to).toBe("vinod@vendor.test");
    expect(result.mailto.startsWith("mailto:vinod%40vendor.test?")).toBe(true);
    // Subject is the PO number; body carries the (mocked) download link.
    expect(decodeURIComponent(result.mailto)).toContain("Purchase Order");
    expect(decodeURIComponent(result.mailto)).toContain("https://files.example/po.pdf?sig=abc");
  });

  it("is available once payment is recorded too (PARTIALLY_PAID)", async () => {
    as(techId, "TECHNICAL");
    const poId = await makePo("PARTIALLY_PAID", vendorWithEmailId);
    expect("ok" in (await prepareVendorEmail(poId))).toBe(true);
  });

  it("refuses a PO that is not yet approved (DRAFT)", async () => {
    as(techId, "TECHNICAL");
    const poId = await makePo("DRAFT", vendorWithEmailId);
    const result = await prepareVendorEmail(poId);
    expect("error" in result).toBe(true);
  });

  it("errors when the vendor has no primary contact email", async () => {
    as(techId, "TECHNICAL");
    const poId = await makePo("MGR_APPROVED", vendorNoEmailId);
    const result = await prepareVendorEmail(poId);
    expect("error" in result).toBe(true);
  });

  it("errors when the PDF service is not configured", async () => {
    vi.mocked(isPdfServiceConfigured).mockReturnValue(false);
    as(techId, "TECHNICAL");
    const poId = await makePo("MGR_APPROVED", vendorWithEmailId);
    const result = await prepareVendorEmail(poId);
    expect("error" in result).toBe(true);
  });

  it("rejects an unauthenticated caller", async () => {
    vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(null);
    const poId = await makePo("MGR_APPROVED", vendorWithEmailId);
    expect(await prepareVendorEmail(poId)).toEqual({ error: "Unauthorized" });
  });
});
