import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction, Updateable } from "../generated/kysely-tailordb";
import {
  InvalidEffectiveRangeError,
  InvalidUnitPriceError,
  PriceListNotFoundError,
  SalespriceruleNotEditableError,
  SalespriceruleNotFoundError,
} from "../lib/errors.generated";

export type UpdateSalesPriceRuleInput = {
  salesPriceRuleId: string;
  salesPriceListId?: string;
  customerId?: string | null;
  channelId?: string | null;
  currencyId?: string;
  unitId?: string;
  minimumQuantity?: number;
  unitPrice?: number;
  effectiveFrom?: Date | null;
  effectiveTo?: Date | null;
  priority?: number;
  notes?: string | null;
};
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateSalesPriceRuleInput & Partial<CF>,
  ctx: CommandContext,
) {
  void ctx;
  const {
    salesPriceRuleId,
    salesPriceListId,
    customerId,
    channelId,
    currencyId,
    unitId,
    minimumQuantity,
    unitPrice,
    effectiveFrom,
    effectiveTo,
    priority,
    notes,
    ...customFields
  } = input;
  const salesPriceRule = await db
    .selectFrom("SalesPriceRule")
    .selectAll()
    .where("id", "=", salesPriceRuleId)
    .forUpdate()
    .executeTakeFirst();
  if (!salesPriceRule) {
    return err(new SalespriceruleNotFoundError(salesPriceRuleId));
  }
  if (!["DRAFT", "ACTIVE"].includes(salesPriceRule.status)) {
    return err(new SalespriceruleNotEditableError(salesPriceRuleId));
  }
  const effectiveSalesPriceListId = salesPriceListId ?? salesPriceRule.salesPriceListId;
  const salesPriceList = await db
    .selectFrom("SalesPriceList")
    .selectAll()
    .where("id", "=", effectiveSalesPriceListId)
    .executeTakeFirst();
  if (!salesPriceList) {
    return err(new PriceListNotFoundError(effectiveSalesPriceListId));
  }
  const effectiveUnitPrice = unitPrice ?? salesPriceRule.unitPrice;
  const effectiveMinimumQuantity = minimumQuantity ?? salesPriceRule.minimumQuantity;
  const mergedEffectiveFrom =
    effectiveFrom === undefined ? salesPriceRule.effectiveFrom : effectiveFrom;
  const mergedEffectiveTo = effectiveTo === undefined ? salesPriceRule.effectiveTo : effectiveTo;
  if (effectiveUnitPrice <= 0 || effectiveMinimumQuantity < 0) {
    return err(new InvalidUnitPriceError(salesPriceRuleId));
  }
  if (mergedEffectiveFrom && mergedEffectiveTo && mergedEffectiveTo < mergedEffectiveFrom) {
    return err(new InvalidEffectiveRangeError(salesPriceRuleId));
  }

  const updates: Updateable<"SalesPriceRule"> = {
    ...(customFields as Updateable<"SalesPriceRule">),
  };
  if (salesPriceListId !== undefined) updates.salesPriceListId = salesPriceListId;
  if (customerId !== undefined) updates.customerId = customerId;
  if (channelId !== undefined) updates.channelId = channelId;
  if (currencyId !== undefined) updates.currencyId = currencyId;
  if (unitId !== undefined) updates.unitId = unitId;
  if (minimumQuantity !== undefined) updates.minimumQuantity = minimumQuantity;
  if (unitPrice !== undefined) updates.unitPrice = unitPrice;
  if (effectiveFrom !== undefined) updates.effectiveFrom = effectiveFrom;
  if (effectiveTo !== undefined) updates.effectiveTo = effectiveTo;
  if (priority !== undefined) updates.priority = priority;
  if (notes !== undefined) updates.notes = notes;

  if (Object.keys(updates).length === 0) {
    return ok({ salesPriceRule });
  }

  const updated = await db
    .updateTable("SalesPriceRule")
    .set(updates)
    .where("id", "=", salesPriceRuleId)
    .returningAll()
    .executeTakeFirstOrThrow();
  return ok({ salesPriceRule: updated });
}
