import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  ApBusinessPartnerNotFoundError,
  CompanyNotFoundError,
  InvalidToleranceValueError,
  InvoiceToleranceConfigAlreadyExistsError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { baseBusinessPartner, baseCompany } from "../testing/fixtures";
import {
  mockBusinessPartnerQueries,
  mockOrganizationQueries,
  type Company,
  type Partner,
} from "../testing/moduleMocks";
import {
  run as runBase,
  type CreateInvoiceToleranceConfigInput,
} from "./createInvoiceToleranceConfig";

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

const run = (
  db: Parameters<typeof runBase>[0],
  input: unknown,
  ctx: Parameters<typeof runBase>[2],
  companies: Company[] = [baseCompany],
  partners: Partner[] = [baseBusinessPartner],
) =>
  runBase(
    db,
    input as Parameters<typeof runBase>[1],
    ctx,
    mockOrganizationQueries({ companies }),
    mockBusinessPartnerQueries({ partners }),
  );

const input: CreateInvoiceToleranceConfigInput = {
  companyId: baseCompany.id,
  supplierId: baseBusinessPartner.id,
  quantityAbsoluteTolerance: "1",
  quantityPercentageTolerance: null,
  unitPriceAbsoluteTolerance: null,
  unitPricePercentageTolerance: "0.05",
};

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("createInvoiceToleranceConfig", () => {
  it("creates supplier invoice tolerance config", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([], [invoiceToleranceConfig]);

    const result = await mock.withTx((trx) => run(trx, input, commandCtx));

    const value = expectOk(result);
    expect(value.invoiceToleranceConfig.id).toBe(invoiceToleranceConfig.id);
    expect(mock.inserts[0].insertRows()).toEqual([
      {
        companyId: baseCompany.id,
        supplierId: baseBusinessPartner.id,
        quantityAbsoluteTolerance: "1",
        quantityPercentageTolerance: null,
        unitPriceAbsoluteTolerance: null,
        unitPricePercentageTolerance: "0.05",
      },
    ]);
  });

  it("passes custom config fields through to the insert", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([], [invoiceToleranceConfig]);

    const result = await mock.withTx((trx) =>
      run(trx, { ...input, approvalTier: "A" }, commandCtx),
    );

    expectOk(result);
    expect(mock.inserts[0].insertRows()).toEqual([expect.objectContaining({ approvalTier: "A" })]);
  });

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

    const result = await mock.withTx((trx) => run(trx, input, commandCtx, []));

    expectErr(result, CompanyNotFoundError);
  });

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

    const result = await mock.withTx((trx) => run(trx, input, commandCtx, [baseCompany], []));

    expectErr(result, ApBusinessPartnerNotFoundError);
  });

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

    const result = await mock.withTx((trx) =>
      run(trx, { ...input, unitPricePercentageTolerance: "-0.01" }, commandCtx),
    );

    expectErr(result, InvalidToleranceValueError);
  });

  it("returns error when config already exists for company and supplier", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([invoiceToleranceConfig]);

    const result = await mock.withTx((trx) => run(trx, input, commandCtx));

    expectErr(result, InvoiceToleranceConfigAlreadyExistsError);
  });
});
