import { err, ok, type ReadonlyDB } from "@tailor-platform/erp-kit/core";
import type { DB } from "../generated/kysely-tailordb";
import { effectiveDateInRange } from "../lib/domain";
import { InvalidPricingContextError } from "../lib/errors.generated";

export interface GetApplicableSalesPriceInput {
  companyId: string;
  itemId: string;
  customerId?: string | null;
  channelId?: string | null;
  currencyId: string;
  unitId: string;
  quantity: number;
  pricingDate?: Date;
}

export async function run(db: ReadonlyDB<DB>, input: GetApplicableSalesPriceInput) {
  if (
    !input.companyId ||
    !input.itemId ||
    !input.currencyId ||
    !input.unitId ||
    input.quantity === undefined ||
    input.quantity < 0
  ) {
    return err(
      new InvalidPricingContextError(input.companyId || input.itemId || "pricing-context"),
    );
  }

  const pricingDate = input.pricingDate ?? new Date();
  const lists = await db
    .selectFrom("SalesPriceList")
    .selectAll()
    .where("companyId", "=", input.companyId)
    .where("status", "=", "ACTIVE")
    .execute();
  const rules = await db
    .selectFrom("SalesPriceRule")
    .selectAll()
    .where("itemId", "=", input.itemId)
    .where("status", "=", "ACTIVE")
    .execute();

  const listById = new Map(lists.map((list) => [list.id, list]));
  const matchingRules = rules
    .flatMap((rule) => {
      const list = listById.get(rule.salesPriceListId);
      return list ? [{ rule, list }] : [];
    })
    .filter(({ rule }) => rule.currencyId === input.currencyId)
    .filter(({ rule }) => rule.unitId === input.unitId)
    .filter(({ rule }) => input.quantity >= rule.minimumQuantity)
    .filter(({ rule }) => effectiveDateInRange(pricingDate, rule.effectiveFrom, rule.effectiveTo))
    .filter(({ rule }) => !rule.customerId || rule.customerId === input.customerId)
    .filter(({ rule }) => !rule.channelId || rule.channelId === input.channelId)
    .sort((left, right) => {
      const leftSpecificity =
        Number(Boolean(left.rule.customerId)) + Number(Boolean(left.rule.channelId));
      const rightSpecificity =
        Number(Boolean(right.rule.customerId)) + Number(Boolean(right.rule.channelId));
      if (leftSpecificity !== rightSpecificity) {
        return rightSpecificity - leftSpecificity;
      }
      if (left.rule.minimumQuantity !== right.rule.minimumQuantity) {
        return right.rule.minimumQuantity - left.rule.minimumQuantity;
      }
      if (left.list.priority !== right.list.priority) {
        return right.list.priority - left.list.priority;
      }
      return right.rule.priority - left.rule.priority;
    });

  const matched = matchingRules[0];
  if (!matched) {
    return ok({ matchedRule: null, priceSuggestion: null });
  }

  return ok({
    matchedRule: matched.rule,
    priceSuggestion: {
      ruleId: matched.rule.id,
      priceListId: matched.rule.salesPriceListId,
      unitPrice: matched.rule.unitPrice,
    },
  });
}
