import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { approvedRequest, pendingRequest, rejectedRequest } from "../testing/fixtures";
import { run } from "./listApprovalRequestsForApprover";

const userId = "user-manager-1";

describe("listApprovalRequestsForApprover", () => {
  it("returns requests where the user is a PENDING assignee on an IN_PROGRESS step with parent request PENDING", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: pendingRequest.id }])
      .mockReturnValueOnce([pendingRequest]);

    const result = await run(db, { userId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([pendingRequest]);
      expect(result.value.hasNextPage).toBe(false);
    }
    expect(spies.where).toHaveBeenCalledWith("ApprovalStepAssignee.userId", "=", userId);
    expect(spies.where).toHaveBeenCalledWith("ApprovalStepAssignee.status", "=", "PENDING");
    expect(spies.where).toHaveBeenCalledWith("ApprovalStep.status", "=", "IN_PROGRESS");
    expect(spies.where).toHaveBeenCalledWith("status", "=", "PENDING");
  });

  it("excludes requests where the user's only assignee row has resolved (APPROVED, REJECTED, DELEGATED)", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, { userId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
    expect(spies.where).toHaveBeenCalledWith("ApprovalStepAssignee.status", "=", "PENDING");
  });

  it("excludes requests where the parent has terminated by WITHDRAW even though the StepAssignee row is still PENDING", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: pendingRequest.id }])
      .mockReturnValueOnce([]);

    const result = await run(db, { userId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
    expect(spies.where).toHaveBeenCalledWith("status", "=", "PENDING");
  });

  it("excludes requests where the parent has terminated by CANCEL even though the StepAssignee row is still PENDING", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: pendingRequest.id }])
      .mockReturnValueOnce([]);

    const result = await run(db, { userId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
    expect(spies.where).toHaveBeenCalledWith("status", "=", "PENDING");
  });

  it("defaults the status filter to PENDING when omitted", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: pendingRequest.id }])
      .mockReturnValueOnce([pendingRequest]);

    await run(db, { userId });

    expect(spies.where).toHaveBeenCalledWith("status", "=", "PENDING");
  });

  it("accepts a non-default status (e.g. APPROVED) to surface historical requests the user worked on", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: approvedRequest.id }])
      .mockReturnValueOnce([approvedRequest]);

    const result = await run(db, { userId, status: "APPROVED" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([approvedRequest]);
    }
    expect(spies.where).toHaveBeenCalledWith("status", "=", "APPROVED");
  });

  it("accepts a non-default status (e.g. REJECTED) to surface historical requests the user worked on", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: rejectedRequest.id }])
      .mockReturnValueOnce([rejectedRequest]);

    const result = await run(db, { userId, status: "REJECTED" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([rejectedRequest]);
    }
    expect(spies.where).toHaveBeenCalledWith("status", "=", "REJECTED");
  });

  it("deduplicates requests where the user has multiple PENDING assignee rows on the same step", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([
        { approvalRequestId: pendingRequest.id },
        { approvalRequestId: pendingRequest.id },
      ])
      .mockReturnValueOnce([pendingRequest]);

    const result = await run(db, { userId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([pendingRequest]);
    }
    // Second-query IN filter should receive a single id (deduped)
    expect(spies.where).toHaveBeenCalledWith("id", "in", [pendingRequest.id]);
  });

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

    const result = await run(db, { userId });

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

  it("returns rows ordered by createdAt DESC", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([{ approvalRequestId: pendingRequest.id }])
      .mockReturnValueOnce([pendingRequest]);

    await run(db, { userId });

    expect(spies.orderBy).toHaveBeenCalledWith("createdAt", "desc");
  });
});
