import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import { Decimal } from "decimal.js";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CurrencyNotFoundError,
  DuplicateRulePrecedenceError,
  InvalidEffectiveRangeError,
  InvalidMinimumQuantityError,
  InvalidUnitPriceError,
  ItemNotActiveError,
  ItemNotFoundError,
  PriceListNotFoundError,
  UnitNotFoundError,
} from "../lib/errors.generated";
import type { ItemManagementQueries, PrimitivesQueries } from "../module";

export interface CreatePurchasePriceRuleInput {
  priceListId: string;
  itemId: string;
  currencyId: string;
  unitId: string;
  minimumQuantity: string;
  unitPrice: string;
  effectiveFrom: Date;
  effectiveTo?: Date | null;
  priority?: number;
  notes?: string;
}

export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreatePurchasePriceRuleInput & CF,
  ctx: CommandContext,
  itemManagementQueries: Pick<ItemManagementQueries, "getItem">,
  primitivesQueries: Pick<PrimitivesQueries, "getCurrency" | "getUnit">,
) {
  const {
    priceListId,
    itemId,
    currencyId,
    unitId,
    minimumQuantity,
    unitPrice,
    effectiveFrom,
    effectiveTo,
    priority,
    notes,
    ...customFields
  } = input;
  if (
    !(await db
      .selectFrom("PurchasePriceList")
      .selectAll()
      .where("id", "=", priceListId)
      .executeTakeFirst())
  ) {
    return err(new PriceListNotFoundError(priceListId));
  }

  const { item } = (await itemManagementQueries.getItem(db, { id: itemId }, ctx)).value;
  if (!item) {
    return err(new ItemNotFoundError(itemId));
  }
  if (item.status !== "ACTIVE") {
    return err(new ItemNotActiveError(itemId));
  }

  const { currency } = (await primitivesQueries.getCurrency(db, { id: currencyId }, ctx)).value;
  if (!currency) {
    return err(new CurrencyNotFoundError(currencyId));
  }
  const { unit } = (await primitivesQueries.getUnit(db, { id: unitId }, ctx)).value;
  if (!unit) {
    return err(new UnitNotFoundError(unitId));
  }
  if (new Decimal(unitPrice).lte(0)) {
    return err(new InvalidUnitPriceError(unitPrice));
  }
  if (new Decimal(minimumQuantity).lt(0)) {
    return err(new InvalidMinimumQuantityError(minimumQuantity));
  }
  if (effectiveTo && effectiveTo < effectiveFrom) {
    return err(new InvalidEffectiveRangeError(itemId));
  }

  const conflictingRule = await db
    .selectFrom("PurchasePriceRule")
    .selectAll()
    .where("itemId", "=", itemId)
    .where("currencyId", "=", currencyId)
    .where("unitId", "=", unitId)
    .where("minimumQuantity", "=", minimumQuantity)
    .where("effectiveFrom", "=", effectiveFrom)
    .where("status", "=", "ACTIVE")
    .executeTakeFirst();
  if (conflictingRule) {
    return err(new DuplicateRulePrecedenceError(itemId));
  }

  const purchasePriceRule = await db
    .insertInto("PurchasePriceRule")
    .values({
      ...(customFields as Record<string, unknown>),
      priceListId,
      itemId,
      currencyId,
      unitId,
      minimumQuantity,
      unitPrice,
      effectiveFrom,
      effectiveTo: effectiveTo ?? null,
      priority: priority ?? 0,
      status: "ACTIVE",
      notes: notes ?? null,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ purchasePriceRule });
}
