import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { RequestNotFoundError } from "../lib/errors.generated";
import { approveDecision, pendingRequest } from "../testing/fixtures";
import { run } from "./getApprovalDecisionHistory";

describe("getApprovalDecisionHistory", () => {
  it("returns decisions ordered by decidedAt ASC", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(pendingRequest).mockReturnValueOnce([approveDecision]);

    const result = await run(db, { approvalRequestId: pendingRequest.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.approvalDecisions).toEqual([approveDecision]);
    }
    expect(spies.orderBy).toHaveBeenCalledWith("decidedAt", "asc");
  });

  it("returns mixed decision rows (APPROVE / REJECT / DELEGATE / WITHDRAW / CANCEL) verbatim", async () => {
    const { db, spies } = createMockDb<DB>();

    const rejectDecision = {
      ...approveDecision,
      id: "decision-reject-1",
      decision: "REJECT" as const,
      comment: "Exceeds Q2 budget cap",
      decidedAt: new Date("2024-03-02T01:00:00.000Z"),
    };
    const delegateDecision = {
      ...approveDecision,
      id: "decision-delegate-1",
      decision: "DELEGATE" as const,
      delegatedToUserId: "user-director-1",
      decidedAt: new Date("2024-03-02T02:00:00.000Z"),
    };
    const withdrawDecision = {
      ...approveDecision,
      id: "decision-withdraw-1",
      approvalStepId: null,
      decision: "WITHDRAW" as const,
      decidedAt: new Date("2024-03-02T03:00:00.000Z"),
    };
    const cancelDecision = {
      ...approveDecision,
      id: "decision-cancel-1",
      approvalStepId: null,
      decision: "CANCEL" as const,
      comment: "Superseded by new request",
      decidedAt: new Date("2024-03-02T04:00:00.000Z"),
    };
    const mixed = [
      approveDecision,
      rejectDecision,
      delegateDecision,
      withdrawDecision,
      cancelDecision,
    ];

    spies.select.mockReturnValueOnce(pendingRequest).mockReturnValueOnce(mixed);

    const result = await run(db, { approvalRequestId: pendingRequest.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.approvalDecisions).toEqual(mixed);
    }
  });

  it("returns an empty array when the request has no decisions yet", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(pendingRequest).mockReturnValueOnce([]);

    const result = await run(db, { approvalRequestId: pendingRequest.id });

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

  it("returns REQUEST_NOT_FOUND when the request does not exist", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { approvalRequestId: "nonexistent-request" });

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