import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import {
  baseAllocationCycle,
  validatedAllocationCycle,
  executedAllocationCycle,
  baseAllocationRule,
  baseAllocationResult,
} from "../testing/fixtures";
import { run } from "./getAllocationCycle";

describe("getAllocationCycle", () => {
  it("returns allocation cycle with all fields and allocation rules when found by id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseAllocationCycle)
      .mockReturnValueOnce([baseAllocationRule])
      .mockReturnValueOnce([]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.allocationCycle).toEqual(baseAllocationCycle);
      expect(result.value.rules).toEqual([baseAllocationRule]);
    }
  });

  it("returns allocation cycle with allocation results when cycle has been executed", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(executedAllocationCycle)
      .mockReturnValueOnce([baseAllocationRule])
      .mockReturnValueOnce([baseAllocationResult]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.allocationCycle?.status).toEqual("EXECUTED");
      expect(result.value.results).toEqual([baseAllocationResult]);
    }
  });

  it("returns allocation cycle with empty results when cycle is in DRAFT or VALIDATED status", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(validatedAllocationCycle)
      .mockReturnValueOnce([baseAllocationRule])
      .mockReturnValueOnce([]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.allocationCycle?.status).toEqual("VALIDATED");
      expect(result.value.results).toEqual([]);
    }
  });

  it("returns allocation rules with sender, receiver, and percentage details", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseAllocationCycle)
      .mockReturnValueOnce([baseAllocationRule])
      .mockReturnValueOnce([]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.rules[0]).toMatchObject({
        senderCostCenterId: baseAllocationRule.senderCostCenterId,
        receiverId: baseAllocationRule.receiverId,
        percentage: baseAllocationRule.percentage,
      });
    }
  });

  it("returns null when no allocation cycle matches the given id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { id: "non-existent" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.allocationCycle).toBeNull();
      expect(result.value.rules).toEqual([]);
      expect(result.value.results).toEqual([]);
    }
  });
});
