import { fromCents, percentageOf, toCents, toPercentHundredths } from "@/lib/domain/money";

/**
 * Cálculo del snapshot económico de una venta.
 *
 * Dos conceptos independientes:
 *   A) Comisión CHESCO por consignación → define el neto a pagar al propietario.
 *   B) Comisión del asesor por realizar la venta → costo de CHESCO, no se descuenta al propietario.
 *
 * Supuestos (confirmar con negocio):
 *   - La comisión porcentual del asesor se calcula sobre el precio final de venta.
 *   - Neto al propietario = precio final − comisión CHESCO.
 *
 * Los montos devueltos se guardan tal cual en la venta y no se recalculan si cambia la configuración.
 */

export type AdvisorCommissionTypeValue = "NONE" | "PERCENTAGE" | "FIXED";
export type ChescoCommissionTypeValue = "PERCENTAGE" | "FIXED";

export type CommissionRule<T extends string> = {
  type: T;
  /** Porcentaje (0–100, hasta 2 decimales) o monto fijo (hasta 2 decimales), como texto. */
  value: string;
};

export type SaleSnapshotInput = {
  finalPrice: string;
  advisorCommission: CommissionRule<AdvisorCommissionTypeValue>;
  /** Solo si el vehículo vendido es consignado. */
  consignment?: {
    commission: CommissionRule<ChescoCommissionTypeValue>;
    minimumPrice: string | null;
  };
};

export type SaleSnapshot = {
  finalPrice: string;
  advisorCommission: {
    type: AdvisorCommissionTypeValue;
    value: string;
    amount: string;
    status: "PENDING" | null;
  };
  consignment: {
    chescoCommissionType: ChescoCommissionTypeValue;
    chescoCommissionValue: string;
    chescoCommissionAmount: string;
    ownerPayoutAmount: string;
    ownerPayoutStatus: "PENDING";
    minimumPrice: string | null;
    belowMinimumPrice: boolean;
  } | null;
};

export type CommissionErrorCode =
  | "INVALID_AMOUNT"
  | "PERCENTAGE_OUT_OF_RANGE"
  | "FIXED_EXCEEDS_PRICE"
  | "CHESCO_COMMISSION_EXCEEDS_PRICE";

export class CommissionError extends Error {
  constructor(
    public readonly code: CommissionErrorCode,
    message: string,
  ) {
    super(message);
    this.name = "CommissionError";
  }
}

function parseAmount(value: string, label: string): bigint {
  try {
    return toCents(value);
  } catch {
    throw new CommissionError("INVALID_AMOUNT", `${label}: monto inválido`);
  }
}

function commissionCents(
  baseCents: bigint,
  rule: CommissionRule<AdvisorCommissionTypeValue | ChescoCommissionTypeValue>,
  label: string,
): { valueText: string; amountCents: bigint } {
  if (rule.type === "NONE") return { valueText: "0.00", amountCents: 0n };

  if (rule.type === "PERCENTAGE") {
    let hundredths: bigint;
    try {
      hundredths = toPercentHundredths(rule.value);
    } catch {
      throw new CommissionError(
        "PERCENTAGE_OUT_OF_RANGE",
        `${label}: el porcentaje debe estar entre 0 y 100, con hasta 2 decimales`,
      );
    }
    return { valueText: fromCents(hundredths), amountCents: percentageOf(baseCents, hundredths) };
  }

  const fixedCents = parseAmount(rule.value, label);
  if (fixedCents > baseCents) {
    throw new CommissionError(
      "FIXED_EXCEEDS_PRICE",
      `${label}: el monto fijo supera el precio final`,
    );
  }
  return { valueText: fromCents(fixedCents), amountCents: fixedCents };
}

export function computeSaleSnapshot(input: SaleSnapshotInput): SaleSnapshot {
  const priceCents = parseAmount(input.finalPrice, "Precio final");

  const advisor = commissionCents(priceCents, input.advisorCommission, "Comisión del asesor");

  let consignment: SaleSnapshot["consignment"] = null;
  if (input.consignment) {
    const chesco = commissionCents(priceCents, input.consignment.commission, "Comisión CHESCO");
    if (chesco.amountCents > priceCents) {
      throw new CommissionError(
        "CHESCO_COMMISSION_EXCEEDS_PRICE",
        "Comisión CHESCO: supera el precio final",
      );
    }
    const minimumCents =
      input.consignment.minimumPrice === null
        ? null
        : parseAmount(input.consignment.minimumPrice, "Precio mínimo");
    consignment = {
      chescoCommissionType: input.consignment.commission.type,
      chescoCommissionValue: chesco.valueText,
      chescoCommissionAmount: fromCents(chesco.amountCents),
      ownerPayoutAmount: fromCents(priceCents - chesco.amountCents),
      ownerPayoutStatus: "PENDING",
      minimumPrice: minimumCents === null ? null : fromCents(minimumCents),
      belowMinimumPrice: minimumCents !== null && priceCents < minimumCents,
    };
  }

  return {
    finalPrice: fromCents(priceCents),
    advisorCommission: {
      type: input.advisorCommission.type,
      value: advisor.valueText,
      amount: fromCents(advisor.amountCents),
      status: input.advisorCommission.type === "NONE" ? null : "PENDING",
    },
    consignment,
  };
}

/**
 * Efecto de anular una venta sobre sus comisiones y la liquidación al propietario:
 * lo PENDIENTE pasa a CANCELADO. Lo ya PAGADO no se toca y se marca para regularización manual.
 */
export function annulmentPayoutChanges(current: {
  advisorCommissionStatus: "PENDING" | "PAID" | "CANCELLED" | null;
  ownerPayoutStatus: "PENDING" | "PAID" | "CANCELLED" | null;
}): {
  advisorCommissionStatus: "PENDING" | "PAID" | "CANCELLED" | null;
  ownerPayoutStatus: "PENDING" | "PAID" | "CANCELLED" | null;
  requiresManualReview: boolean;
} {
  const cancelIfPending = <T extends string | null>(status: T): T | "CANCELLED" =>
    status === "PENDING" ? "CANCELLED" : status;
  return {
    advisorCommissionStatus: cancelIfPending(current.advisorCommissionStatus),
    ownerPayoutStatus: cancelIfPending(current.ownerPayoutStatus),
    requiresManualReview:
      current.advisorCommissionStatus === "PAID" || current.ownerPayoutStatus === "PAID",
  };
}
