import { InsufficientPermissionError } from "@tailor-platform/erp-kit/core";
import type { QueryContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { UnauthorizedError } from "../lib/errors.generated";
import { baseAuditEntry, globalAuditEntry } from "../testing/fixtures";
import { run } from "./getAuditEntry";

const ctx: QueryContext = {
  actorId: "test-actor",
  permissions: ["audit:audit:viewAuditHistory"],
};

describe("getAuditEntry", () => {
  it("returns audit entry when found by id", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseAuditEntry]);

    const result = await run(mock.db, { id: "entry-1" }, ctx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.auditEntry).toEqual(baseAuditEntry);
    }
  });

  it("returns null when audit entry not found", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await run(mock.db, { id: "nonexistent" }, ctx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.auditEntry).toBeNull();
    }
  });

  it("returns entry with all fields populated", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([globalAuditEntry]);

    const result = await run(mock.db, { id: globalAuditEntry.id }, ctx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      const entry = result.value.auditEntry;
      expect(entry).toEqual(globalAuditEntry);
      expect(entry!.correlationId).toBe("corr-uuid-1");
      expect(entry!.ipAddress).toBe("192.168.1.1");
      expect(entry!.userAgent).toBe("AuditBot/1.0");
      expect(entry!.sessionId).toBe("session-1");
      expect(entry!.requestId).toBe("req-1");
      expect(entry!.onBehalfOf).toBe("user-3");
    }
  });

  it("returns company-scoped entry when caller has matching company permission", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseAuditEntry]);
    const companyScopedCtx: QueryContext = {
      actorId: "test-actor",
      permissions: ["audit:audit:viewAuditHistory"],
      companyId: "company-1",
    };

    const result = await run(mock.db, { id: baseAuditEntry.id }, companyScopedCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.auditEntry).toEqual(baseAuditEntry);
      expect(result.value.auditEntry!.companyId).toBe("company-1");
    }
  });

  it("returns global entry when caller has global-scoped permission", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([globalAuditEntry]);
    const globalScopedCtx: QueryContext = {
      actorId: "test-actor",
      permissions: ["audit:audit:viewAuditHistory"],
      companyId: null,
    };

    const result = await run(mock.db, { id: globalAuditEntry.id }, globalScopedCtx);

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.auditEntry).toEqual(globalAuditEntry);
      expect(result.value.auditEntry!.companyId).toBeNull();
    }
  });

  it("rejects when caller lacks viewAuditHistory permission", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const noPermsCtx: QueryContext = { actorId: "test-actor", permissions: [] };

    const result = await run(mock.db, { id: "entry-1" }, noPermsCtx);

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InsufficientPermissionError);
    }
  });

  it("rejects when caller lacks permission for entry's company scope", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseAuditEntry]); // entry with companyId: "company-1"
    const scopedCtx: QueryContext = {
      actorId: "test-actor",
      permissions: ["audit:audit:viewAuditHistory"],
      companyId: "company-2", // different company
    };

    const result = await run(mock.db, { id: baseAuditEntry.id }, scopedCtx);

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(UnauthorizedError);
    }
  });
});
