import type { ReadonlyDB } from "@tailor-platform/erp-kit/core";
import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { activePolicy, draftPolicy, inactivePolicy } from "../testing/fixtures";
import { run } from "./listApprovalPolicies";

describe("listApprovalPolicies", () => {
  it("returns all ACTIVE policies when status = ACTIVE filter is supplied", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([activePolicy]);

    const result = await run(db as ReadonlyDB<DB>, { status: "ACTIVE" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([activePolicy]);
      expect(result.value.hasNextPage).toBe(false);
    }
  });

  it("returns only policies matching the supplied purpose", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([draftPolicy, activePolicy, inactivePolicy]);

    const result = await run(db as ReadonlyDB<DB>, { purpose: "PRODUCT_ACTIVATION" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(3);
      expect(result.value.items.every((p) => p.purpose === "PRODUCT_ACTIVATION")).toBe(true);
    }
  });

  it("returns combinations of purpose and status filters", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([activePolicy]);

    const result = await run(db as ReadonlyDB<DB>, {
      purpose: "PRODUCT_ACTIVATION",
      status: "ACTIVE",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([activePolicy]);
    }
  });

  it("returns all statuses when status filter is omitted", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([draftPolicy, activePolicy, inactivePolicy]);

    const result = await run(db as ReadonlyDB<DB>, {});

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(3);
      const statuses = result.value.items.map((p) => p.status);
      expect(statuses).toEqual(expect.arrayContaining(["DRAFT", "ACTIVE", "INACTIVE"]));
    }
  });

  it("returns all purposes when purpose filter is omitted", async () => {
    const { db, spies } = createMockDb<DB>();
    const otherPurposePolicy = { ...activePolicy, id: "policy-other", purpose: "OTHER" };
    spies.select.mockReturnValueOnce([activePolicy, otherPurposePolicy]);

    const result = await run(db as ReadonlyDB<DB>, {});

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
      const purposes = result.value.items.map((p) => p.purpose);
      expect(purposes).toEqual(expect.arrayContaining(["PRODUCT_ACTIVATION", "OTHER"]));
    }
  });

  it("returns an empty array when no policies match", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([]);

    const result = await run(db as ReadonlyDB<DB>, { purpose: "NO_MATCH" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
      expect(result.value.hasNextPage).toBe(false);
    }
  });

  it("includes both DRAFT and INACTIVE rows when no implicit hide is applied", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([draftPolicy, inactivePolicy]);

    const result = await run(db as ReadonlyDB<DB>, {});

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
      const statuses = result.value.items.map((p) => p.status);
      expect(statuses).toEqual(expect.arrayContaining(["DRAFT", "INACTIVE"]));
    }
  });

  it("orders by (purpose ASC, name ASC, activatedAt DESC) when no orderBy is supplied", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([]);

    await run(db as ReadonlyDB<DB>, {});

    const orderByCalls = spies.orderBy.mock.calls;
    expect(orderByCalls.length).toBeGreaterThanOrEqual(3);
    expect(orderByCalls[0]?.[0]).toBe("purpose");
    expect(orderByCalls[0]?.[1]).toBe("asc");
    expect(orderByCalls[1]?.[0]).toBe("name");
    expect(orderByCalls[1]?.[1]).toBe("asc");
    expect(orderByCalls[2]?.[0]).toBe("activatedAt");
    expect(orderByCalls[2]?.[1]).toBe("desc");
  });
});
