import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import { Decimal } from "decimal.js";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ApBusinessPartnerNotFoundError,
  CompanyNotFoundError,
  InvalidToleranceValueError,
  InvoiceToleranceConfigAlreadyExistsError,
} from "../lib/errors.generated";
import type { BusinessPartnerQueries, OrganizationQueries } from "../module";

export interface CreateInvoiceToleranceConfigInput {
  companyId: string;
  supplierId: string;
  quantityAbsoluteTolerance?: string | null;
  quantityPercentageTolerance?: string | null;
  unitPriceAbsoluteTolerance?: string | null;
  unitPricePercentageTolerance?: string | null;
}

const toleranceFields = [
  "quantityAbsoluteTolerance",
  "quantityPercentageTolerance",
  "unitPriceAbsoluteTolerance",
  "unitPricePercentageTolerance",
] as const;

function validateToleranceValues(input: CreateInvoiceToleranceConfigInput) {
  for (const field of toleranceFields) {
    const value = input[field];
    if (value != null && new Decimal(value).lt(0)) {
      return err(new InvalidToleranceValueError(field));
    }
  }
  return ok({});
}

export async function run<F extends object = object>(
  db: Transaction,
  input: CreateInvoiceToleranceConfigInput & F,
  ctx: CommandContext,
  organizationQueries: Pick<OrganizationQueries, "getCompany">,
  businessPartnerQueries: Pick<BusinessPartnerQueries, "getPartner">,
) {
  const {
    companyId,
    supplierId,
    quantityAbsoluteTolerance,
    quantityPercentageTolerance,
    unitPriceAbsoluteTolerance,
    unitPricePercentageTolerance,
    ...customFields
  } = input;

  const { company } = (await organizationQueries.getCompany(db, { id: companyId }, ctx)).value;
  if (!company) return err(new CompanyNotFoundError(companyId));

  const { partner: supplier } = (
    await businessPartnerQueries.getPartner(db, { id: supplierId }, ctx)
  ).value;
  if (!supplier) return err(new ApBusinessPartnerNotFoundError(supplierId));

  const toleranceResult = validateToleranceValues(input);
  if (!toleranceResult.ok) return toleranceResult;

  const existing = await db
    .selectFrom("InvoiceToleranceConfig")
    .selectAll()
    .where("companyId", "=", companyId)
    .where("supplierId", "=", supplierId)
    .executeTakeFirst();
  if (existing) {
    return err(new InvoiceToleranceConfigAlreadyExistsError(`${companyId}:${supplierId}`));
  }

  const invoiceToleranceConfig = await db
    .insertInto("InvoiceToleranceConfig")
    .values({
      ...(customFields as Record<string, unknown>),
      companyId,
      supplierId,
      quantityAbsoluteTolerance: quantityAbsoluteTolerance ?? null,
      quantityPercentageTolerance: quantityPercentageTolerance ?? null,
      unitPriceAbsoluteTolerance: unitPriceAbsoluteTolerance ?? null,
      unitPricePercentageTolerance: unitPricePercentageTolerance ?? null,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ invoiceToleranceConfig });
}
