import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  approvedRequest,
  cancelledRequest,
  pendingRequest,
  rejectedRequest,
  withdrawnRequest,
} from "../testing/fixtures";
import { run } from "./listApprovalRequestsByTarget";

describe("listApprovalRequestsByTarget", () => {
  it("returns all requests for a given target — terminal and non-terminal — when no status filter is supplied", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([
      pendingRequest,
      approvedRequest,
      rejectedRequest,
      withdrawnRequest,
      cancelledRequest,
    ]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-100",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(5);
      const statuses = result.value.items.map((r) => r.status);
      expect(statuses).toEqual(
        expect.arrayContaining(["PENDING", "APPROVED", "REJECTED", "WITHDRAWN", "CANCELLED"]),
      );
      expect(result.value.hasNextPage).toBe(false);
    }
  });

  it("filters to a single status when the optional filter is supplied", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([approvedRequest]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-300",
      status: "APPROVED",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0].status).toBe("APPROVED");
    }
  });

  it("returns terminal requests indefinitely (no time-window cutoff)", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    // Resolved long before "now" — a time-bound query would have filtered these out.
    mock.enqueueResults([approvedRequest, rejectedRequest, cancelledRequest]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-300",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(3);
      const statuses = result.value.items.map((r) => r.status);
      expect(statuses).toEqual(["APPROVED", "REJECTED", "CANCELLED"]);
    }
  });

  it("returns an empty array when the target has never had a request", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-never-requested",
    });

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

  it("returns requests in createdAt DESC order so the most recent appears first", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    // Mock returns DESC-ordered rows; the query is responsible for asking the DB to sort that way.
    const newer = {
      ...pendingRequest,
      id: "request-newer",
      createdAt: new Date("2024-05-01T00:00:00.000Z"),
    };
    const older = {
      ...approvedRequest,
      id: "request-older",
      createdAt: new Date("2024-01-01T00:00:00.000Z"),
    };
    mock.enqueueResults([newer, older]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-100",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items[0].id).toBe("request-newer");
      expect(result.value.items[1].id).toBe("request-older");
      expect(result.value.items[0].createdAt.getTime()).toBeGreaterThan(
        result.value.items[1].createdAt.getTime(),
      );
    }
    expect(mock.selects[0].sql).toMatch(/order by .*"createdAt" desc/i);
  });

  it("includes both policy-mode and direct-mode requests in the result", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const directModeRequest = {
      ...pendingRequest,
      id: "request-direct-1",
      sourcePolicyId: null,
    };
    mock.enqueueResults([pendingRequest, directModeRequest]);

    const result = await run(mock.db, {
      targetEntityType: "Product",
      targetEntityId: "product-200",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
      const sourcePolicyIds = result.value.items.map((r) => r.sourcePolicyId);
      expect(sourcePolicyIds).toContain("policy-active-1");
      expect(sourcePolicyIds).toContain(null);
    }
  });
});
