import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import { AllocationRuleNotFoundError, InvalidStateError } from "../lib/errors.generated";
import {
  baseAllocationRule,
  baseAllocationCycle,
  validatedAllocationCycle,
  executedAllocationCycle,
  postedAllocationCycle,
  reversedAllocationCycle,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./deleteAllocationRule";

describe("deleteAllocationRule", () => {
  it("deletes an allocation rule from a DRAFT allocation cycle", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseAllocationRule) // rule lookup
      .mockReturnValueOnce(baseAllocationCycle); // cycle lookup

    const result = await run(db, { allocationRuleId: baseAllocationRule.id }, commandCtx);

    const value = expectOk(result);
    expect(value.allocationRuleId).toBe(baseAllocationRule.id);
  });

  it("returns error when allocation rule does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { allocationRuleId: "missing-id" }, commandCtx);

    expectErr(result, AllocationRuleNotFoundError);
  });

  it("returns error when parent cycle is in VALIDATED status", async () => {
    const ruleForValidated = {
      ...baseAllocationRule,
      allocationCycleId: validatedAllocationCycle.id,
    };
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(ruleForValidated)
      .mockReturnValueOnce(validatedAllocationCycle);

    const result = await run(db, { allocationRuleId: ruleForValidated.id }, commandCtx);

    expectErr(result, InvalidStateError);
  });

  it("returns error when parent cycle is in EXECUTED status", async () => {
    const ruleForExecuted = {
      ...baseAllocationRule,
      allocationCycleId: executedAllocationCycle.id,
    };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(ruleForExecuted).mockReturnValueOnce(executedAllocationCycle);

    const result = await run(db, { allocationRuleId: ruleForExecuted.id }, commandCtx);

    expectErr(result, InvalidStateError);
  });

  it("returns error when parent cycle is in POSTED status", async () => {
    const ruleForPosted = { ...baseAllocationRule, allocationCycleId: postedAllocationCycle.id };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(ruleForPosted).mockReturnValueOnce(postedAllocationCycle);

    const result = await run(db, { allocationRuleId: ruleForPosted.id }, commandCtx);

    expectErr(result, InvalidStateError);
  });

  it("returns error when parent cycle is in REVERSED status", async () => {
    const ruleForReversed = {
      ...baseAllocationRule,
      allocationCycleId: reversedAllocationCycle.id,
    };
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(ruleForReversed).mockReturnValueOnce(reversedAllocationCycle);

    const result = await run(db, { allocationRuleId: ruleForReversed.id }, commandCtx);

    expectErr(result, InvalidStateError);
  });

  it("emits audit event recording the rule deletion and acting user", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(baseAllocationRule).mockReturnValueOnce(baseAllocationCycle);

    const result = await run(db, { allocationRuleId: baseAllocationRule.id }, commandCtx);

    expectOk(result);
    expect(spies.delete).toHaveBeenCalled();
  });
});
