import type { CommandContext } from "@tailor-platform/erp-kit/core";
import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import { InvalidStatusError } from "../lib/errors.generated";
import { expectErr, expectOk } from "../testing/commandTestUtils";
import { baseGoodsIssueExecution, baseGoodsReceiptExecution } from "../testing/fixtures";
import { run } from "./cancelGoodsReceipt";

describe("cancelGoodsReceipt", () => {
  const ctx: CommandContext = {
    actorId: "test-actor",
    permissions: ["inventory:inventoryOperation:cancelGoodsReceipt"],
  };

  it("returns error when receipt execution is missing", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await mock.withTx((trx) => run(trx, { id: "missing" }, ctx));

    expectErr(result as never, InvalidStatusError);
    expect(mock.updates).toHaveLength(0);
  });

  it("returns error when execution is not a goods receipt", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseGoodsIssueExecution]);

    const result = await mock.withTx((trx) => run(trx, { id: baseGoodsIssueExecution.id }, ctx));

    expectErr(result as never, InvalidStatusError);
    expect(mock.updates).toHaveLength(0);
  });

  it("returns error when receipt execution is posted", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...baseGoodsReceiptExecution, status: "POSTED" }]);

    const result = await mock.withTx((trx) => run(trx, { id: baseGoodsReceiptExecution.id }, ctx));

    expectErr(result as never, InvalidStatusError);
    expect(mock.updates).toHaveLength(0);
  });

  it("transitions draft receipt execution to CANCELLED without deleting lines", async () => {
    const cancelledExecution = { ...baseGoodsReceiptExecution, status: "CANCELLED" };

    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseGoodsReceiptExecution], [cancelledExecution]);

    const result = await mock.withTx((trx) => run(trx, { id: baseGoodsReceiptExecution.id }, ctx));

    const value = expectOk<{ inventoryExecution: { status: string } }>(result as never);

    expect(value.inventoryExecution.status).toBe("CANCELLED");
    expect(mock.updates[0].updateValues()).toEqual(
      expect.objectContaining({ status: "CANCELLED" }),
    );
    expect(mock.deletes).toHaveLength(0);
  });
});
