import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  InvalidReservationQuantitiesError,
  StockReservationNotFoundError,
  StockReservationNotOpenError,
} from "../lib/errors.generated";
import { expectOk } from "../testing/commandTestUtils";
import { baseStockReservation } from "../testing/fixtures";
import {
  consumeStockReservationInternal,
  getOpenStockReservationQuantity,
  sumOpenStockReservationQuantity,
} from "./stockReservation";

describe("stockReservation internal helpers", () => {
  it("calculates open reservation quantity", () => {
    expect(
      getOpenStockReservationQuantity({
        reservedQuantity: "10",
        consumedQuantity: "4",
      }).toString(),
    ).toBe("6");
    expect(
      getOpenStockReservationQuantity({
        reservedQuantity: "4",
        consumedQuantity: "10",
      }).toString(),
    ).toBe("0");
  });

  it("sums open reservation quantity", () => {
    expect(
      sumOpenStockReservationQuantity([
        { reservedQuantity: "10", consumedQuantity: "4" },
        { reservedQuantity: "3", consumedQuantity: "0" },
      ]).toString(),
    ).toBe("9");
  });

  it("consumes reservation internally", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const updatedReservation = { ...baseStockReservation, consumedQuantity: "4", status: "OPEN" };
    mock.enqueueResults([baseStockReservation], [updatedReservation]);

    const result = await mock.withTx((trx) =>
      consumeStockReservationInternal(trx, {
        id: "stock-reservation-1",
        consumedQuantity: "4",
      }),
    );
    const { stockReservation } = expectOk<{
      stockReservation: { consumedQuantity: string; status: string };
    }>(result as never);

    expect(stockReservation.consumedQuantity).toBe("4");
    expect(stockReservation.status).toBe("OPEN");
  });

  it("returns error when reservation not found", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await mock.withTx((trx) =>
      consumeStockReservationInternal(trx, {
        id: "stock-reservation-1",
        consumedQuantity: "4",
      }),
    );

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

  it("returns error when consumed quantity is invalid", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();

    const result = await mock.withTx((trx) =>
      consumeStockReservationInternal(trx, {
        id: "stock-reservation-1",
        consumedQuantity: "0",
      }),
    );

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

  it("returns error when reservation is not open", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...baseStockReservation, status: "CLOSED" }]);

    const result = await mock.withTx((trx) =>
      consumeStockReservationInternal(trx, {
        id: "stock-reservation-1",
        consumedQuantity: "4",
      }),
    );

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