/**
 * Integration tests for company branding actions (logo + stamp uploads).
 * Covers:
 *   - Manager can upload a logo / stamp; the key is stored on the company
 *   - Re-upload overwrites in place (deterministic key)
 *   - Invalid asset type, bad mime, and oversize files are rejected
 *   - removeCompanyAsset clears the key
 *   - Permission gating (TECHNICAL cannot manage branding)
 */
import { vi, describe, it, expect, beforeAll, afterAll } from "vitest";

vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("@/lib/storage", async (importOriginal) => ({
  ...(await importOriginal<typeof import("@/lib/storage")>()),
  uploadBuffer: vi.fn(),               // don't touch the filesystem in tests
}));

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { uploadBuffer } from "@/lib/storage";
import { uploadCompanyAsset, removeCompanyAsset } from "@/app/(portal)/admin/companies/actions";
import { makeSession } from "./helpers";

const mockedAuth = vi.mocked(auth);
const mockedUpload = vi.mocked(uploadBuffer);

let companyId: string;

function pngFile(name: string, bytes = 1024): File {
  return new File([new Uint8Array(bytes)], name, { type: "image/png" });
}

function assetForm(id: string, type: string, file: File): FormData {
  const form = new FormData();
  form.set("companyId", id);
  form.set("type", type);
  form.set("file", file);
  return form;
}

beforeAll(async () => {
  const company = await db.company.create({
    data: { name: "INTTEST_BRANDING_CO", code: "ZZBRAND" },
  });
  companyId = company.id;
});

afterAll(async () => {
  await db.company.delete({ where: { id: companyId } }).catch(() => {});
});

describe("uploadCompanyAsset", () => {
  it("stores a logo key on the company", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const res = await uploadCompanyAsset(assetForm(companyId, "logo", pngFile("logo.png")));
    expect(res).toEqual({ ok: true });
    const c = await db.company.findUniqueOrThrow({ where: { id: companyId } });
    expect(c.logoKey).toBe(`company-assets/${companyId}/logo.png`);
    expect(mockedUpload).toHaveBeenCalled();
  });

  it("stores a stamp key independently of the logo", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const res = await uploadCompanyAsset(assetForm(companyId, "stamp", pngFile("stamp.png")));
    expect(res).toEqual({ ok: true });
    const c = await db.company.findUniqueOrThrow({ where: { id: companyId } });
    expect(c.stampKey).toBe(`company-assets/${companyId}/stamp.png`);
    expect(c.logoKey).toBe(`company-assets/${companyId}/logo.png`);
  });

  it("rejects an unknown asset type", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const res = await uploadCompanyAsset(assetForm(companyId, "header", pngFile("x.png")));
    expect(res).toEqual({ error: "Invalid asset type" });
  });

  it("rejects a non-image mime type", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const pdf = new File([new Uint8Array(10)], "x.pdf", { type: "application/pdf" });
    const res = await uploadCompanyAsset(assetForm(companyId, "logo", pdf));
    expect(res).toEqual({ error: "Image must be a PNG, JPG, or WebP" });
  });

  it("rejects a file over 4 MB", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const big = pngFile("big.png", 5 * 1024 * 1024);
    const res = await uploadCompanyAsset(assetForm(companyId, "logo", big));
    expect(res).toEqual({ error: "Image must be under 4 MB" });
  });

  it("refuses callers without manage_vessels_accounts", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never);
    const res = await uploadCompanyAsset(assetForm(companyId, "logo", pngFile("logo.png")));
    expect(res).toEqual({ error: "Unauthorized" });
  });
});

describe("removeCompanyAsset", () => {
  it("clears the stored key", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never);
    const res = await removeCompanyAsset(companyId, "logo");
    expect(res).toEqual({ ok: true });
    const c = await db.company.findUniqueOrThrow({ where: { id: companyId } });
    expect(c.logoKey).toBeNull();
    expect(c.stampKey).toBe(`company-assets/${companyId}/stamp.png`); // stamp untouched
  });

  it("refuses unauthorized callers", async () => {
    mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never);
    const res = await removeCompanyAsset(companyId, "stamp");
    expect(res).toEqual({ error: "Unauthorized" });
  });
});
