import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  InvalidToleranceValueError,
  InvoiceToleranceConfigNotFoundError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { baseBusinessPartner, baseCompany } from "../testing/fixtures";
import { run, type UpdateInvoiceToleranceConfigInput } from "./updateInvoiceToleranceConfig";

const createdAt = new Date("2024-01-01T00:00:00.000Z");

const invoiceToleranceConfig = {
  id: "invoice-tolerance-1",
  companyId: baseCompany.id,
  supplierId: baseBusinessPartner.id,
  quantityAbsoluteTolerance: "1",
  quantityPercentageTolerance: null,
  unitPriceAbsoluteTolerance: null,
  unitPricePercentageTolerance: "0.05",
  createdAt,
  updatedAt: null,
};

describe("updateInvoiceToleranceConfig", () => {
  it("updates invoice tolerance config", async () => {
    const updated = { ...invoiceToleranceConfig, unitPricePercentageTolerance: "0.08" };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([invoiceToleranceConfig], [updated]);

    const input: UpdateInvoiceToleranceConfigInput = {
      companyId: baseCompany.id,
      supplierId: baseBusinessPartner.id,
      patch: { unitPricePercentageTolerance: "0.08" },
    };
    const result = await mock.withTx((trx) => run(trx, input, commandCtx));

    expect(expectOk(result).invoiceToleranceConfig.unitPricePercentageTolerance).toBe("0.08");
    expect(mock.updates[0].updateValues()).toEqual({ unitPricePercentageTolerance: "0.08" });
  });

  it("clears invoice tolerance config fields with null", async () => {
    const updated = { ...invoiceToleranceConfig, quantityAbsoluteTolerance: null };
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([invoiceToleranceConfig], [updated]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          supplierId: baseBusinessPartner.id,
          patch: {
            quantityAbsoluteTolerance: null,
            unitPriceAbsoluteTolerance: undefined,
          },
        },
        commandCtx,
      ),
    );

    expectOk(result);
    expect(mock.updates[0].updateValues()).toEqual({ quantityAbsoluteTolerance: null });
  });

  it("returns the existing config when no fields are changed", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([invoiceToleranceConfig]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { companyId: baseCompany.id, supplierId: baseBusinessPartner.id, patch: {} },
        commandCtx,
      ),
    );

    expect(expectOk(result).invoiceToleranceConfig).toEqual(invoiceToleranceConfig);
    expect(mock.updates).toHaveLength(0);
  });

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

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { companyId: baseCompany.id, supplierId: baseBusinessPartner.id, patch: {} },
        commandCtx,
      ),
    );

    expectErr(result, InvoiceToleranceConfigNotFoundError);
  });

  it("returns error when tolerance value is negative", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([invoiceToleranceConfig]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        {
          companyId: baseCompany.id,
          supplierId: baseBusinessPartner.id,
          patch: { quantityPercentageTolerance: "-0.01" },
        },
        commandCtx,
      ),
    );

    expectErr(result, InvalidToleranceValueError);
  });
});
