import type { Freight } from "@edr/types";
import type { ShipmentFormValues } from "./schema";

export interface ShipmentTotalLine {
  label: string;
  unitPrice: number;
  unit: Freight.ContractRateUnit | string;
  quantity: number;
  amount: number;
}

export interface ShipmentTotal {
  currency: string;
  lines: ShipmentTotalLine[];
  total: number;
}

/**
 * Compute the booking total client-side from the contract's frozen unit rates ×
 * the quantities the customer enters (doc §9.2). This is an estimate shown in
 * the review step; the server recomputes the authoritative total on submit.
 */
export function computeShipmentTotal(
  contract: Freight.IContract,
  values: ShipmentFormValues,
): ShipmentTotal {
  const breakdown = contract.pricingBreakdown;
  const currency = breakdown?.currency ?? contract.paymentCurrency ?? "ETB";
  const items = breakdown?.lineItems ?? [];
  const lines: ShipmentTotalLine[] = [];

  const isContainer = contract.freightType === "CONTAINER";

  const rateFor = (
    predicate: (i: Freight.ContractUnitRateLineItem) => boolean,
  ) => items.find(predicate);

  if (isContainer) {
    let hazardTotalQty = 0;
    let reeferTotalQty = 0;
    let returnTotalQty = 0;

    for (const line of values.containers) {
      const qty = Number(line.quantity || 0);
      if (qty <= 0) continue;
      const rate =
        rateFor(
          (i) =>
            i.containerSize === line.containerSize &&
            i.unit === "per_container" &&
            !i.conditionalOn &&
            !i.isClearance,
        ) ??
        rateFor(
          (i) => i.containerSize === line.containerSize && !i.isClearance,
        );
      if (rate) {
        // A per-wagon rate bills the wagons this line occupies — two 20ft share
        // one — not the box count. Mirrors the API's base-freight line so the
        // quote matches the invoice.
        const billedQty =
          rate.unit === "per_wagon"
            ? Math.ceil(qty * (line.containerSize === "40ft" ? 1 : 0.5))
            : qty;
        lines.push({
          label: rate.label,
          unitPrice: rate.unitPrice,
          unit: rate.unit,
          quantity: billedQty,
          amount: rate.unitPrice * billedQty,
        });
      }
      hazardTotalQty += Number(line.hazardousQuantity || 0);
      reeferTotalQty += Number(line.reeferQuantity || 0);
      returnTotalQty += Number(line.returnQuantity || 0);
    }

    if (contract.isHazardous && hazardTotalQty > 0) {
      const hz = rateFor((i) => i.conditionalOn === "is_hazardous");
      if (hz) {
        lines.push({
          label: hz.label,
          unitPrice: hz.unitPrice,
          unit: hz.unit,
          quantity: hazardTotalQty,
          amount: hz.unitPrice * hazardTotalQty,
        });
      }
    }
    if (contract.isReefer && reeferTotalQty > 0) {
      const rf = rateFor((i) => i.conditionalOn === "is_reefer");
      if (rf) {
        lines.push({
          label: rf.label,
          unitPrice: rf.unitPrice,
          unit: rf.unit,
          quantity: reeferTotalQty,
          amount: rf.unitPrice * reeferTotalQty,
        });
      }
    }
    // Empty-container return is a container-only surcharge, priced per returning
    // container rather than per line (contract-pricing.service emits the
    // `with_return` rate only for WITH_RETURN contracts).
    if (contract.equipmentReturn === "WITH_RETURN" && returnTotalQty > 0) {
      const wr = rateFor((i) => i.conditionalOn === "with_return");
      if (wr) {
        lines.push({
          label: wr.label,
          unitPrice: wr.unitPrice,
          unit: wr.unit,
          quantity: returnTotalQty,
          amount: wr.unitPrice * returnTotalQty,
        });
      }
    }
  } else {
    const qty = Number(values.cargoWeightTons || values.itemCount || 0);
    const rate =
      rateFor(
        (i) =>
          (i.unit === "per_ton" || i.unit === "per_item") &&
          !i.isClearance &&
          !i.conditionalOn,
      ) ?? items[0];
    if (rate && qty > 0) {
      lines.push({
        label: rate.label,
        unitPrice: rate.unitPrice,
        unit: rate.unit,
        quantity: qty,
        amount: rate.unitPrice * qty,
      });
    }
    const hazardQty = Number(values.bulkHazardousQuantity || 0);
    const reeferQty = Number(values.bulkReeferQuantity || 0);
    if (contract.isHazardous && hazardQty > 0) {
      const hz = rateFor((i) => i.conditionalOn === "is_hazardous");
      if (hz) {
        lines.push({
          label: hz.label,
          unitPrice: hz.unitPrice,
          unit: hz.unit,
          quantity: hazardQty,
          amount: hz.unitPrice * hazardQty,
        });
      }
    }
    if (contract.isReefer && reeferQty > 0) {
      const rf = rateFor((i) => i.conditionalOn === "is_reefer");
      if (rf) {
        lines.push({
          label: rf.label,
          unitPrice: rf.unitPrice,
          unit: rf.unit,
          quantity: reeferQty,
          amount: rf.unitPrice * reeferQty,
        });
      }
    }
  }

  // Lashing / cargo securing — bulk-only, applies whenever the contract shows
  // it (the commodity needs lashing). Per-ton/per-item scales by the cargo
  // amount; per-wagon depends on the wagon capacity the train stocks — shown at
  // real pricing.
  const lashing = items.find((i) => i.conditionalOn === "has_lashing");
  if (lashing && (lashing.unit === "per_ton" || lashing.unit === "per_item")) {
    const tons = Number(values.cargoWeightTons || values.itemCount || 0);
    if (tons > 0) {
      lines.push({
        label: lashing.label,
        unitPrice: lashing.unitPrice,
        unit: lashing.unit,
        quantity: tons,
        amount: lashing.unitPrice * tons,
      });
    }
  }

  // Customs clearance service fee — billed on the booking invoice with the
  // freight. Container fees estimate per size (per box, or per wagon: two 20ft
  // share one); bulk per-ton scales by tonnage. Bulk per-wagon fees depend on
  // the wagon capacity the train stocks — shown at real pricing, not estimated.
  for (const cl of items.filter((i) => i.isClearance)) {
    let qty = 0;
    if (isContainer) {
      const boxes = (values.containers ?? [])
        .filter((c) => c.containerSize === cl.containerSize)
        .reduce((s, c) => s + Number(c.quantity || 0), 0);
      qty =
        cl.unit === "per_wagon"
          ? Math.ceil(boxes * (cl.containerSize === "40ft" ? 1 : 0.5))
          : boxes;
    } else if (cl.unit === "per_ton" || cl.unit === "per_item") {
      qty = Number(values.cargoWeightTons || values.itemCount || 0);
    } else if (cl.unit === "flat") {
      qty = 1;
    }
    if (qty > 0) {
      lines.push({
        label: cl.label,
        unitPrice: cl.unitPrice,
        unit: cl.unit,
        quantity: qty,
        amount: cl.unitPrice * qty,
      });
    }
  }

  const total = lines.reduce((s, l) => s + l.amount, 0);
  return { currency, lines, total };
}
