import { format } from "date-fns";

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

import { rawServiceType, type BookingDetail } from "./booking-detail-types";

export const isNegative = (s: string) => s === "CANCELLED" || s === "REJECTED";
export const isDraftLike = (s: string) =>
  s === "DRAFT" || s === "CHANGES_REQUESTED";

/**
 * Terminal booking statuses — nothing changes server-side once a booking lands
 * here, so the customer view has no reason to keep polling.
 */
const SETTLED_STATUSES = new Set([
  "COMPLETED",
  "DELIVERED",
  "CANCELLED",
  "REJECTED",
]);

/**
 * True while a booking can still change from a staff/system action the customer
 * did not trigger (operations accepting an order, batch selection, clearance
 * review, transit progress). Used to poll the customer-facing booking queries so
 * those transitions surface without a manual reload — e.g. an accepted operation
 * request flipping out of "under review".
 */
export const isBookingLive = (s?: string | null) =>
  !!s && !SETTLED_STATUSES.has(s);

export function fmtDate(value?: string | null) {
  if (!value) return "—";
  const d = new Date(value);
  return Number.isNaN(d.getTime()) ? "—" : format(d, "MMM d, yyyy");
}

export function yardLabel(y?: Freight.IBooking["originYard"]) {
  return y?.label ?? y?.code ?? "—";
}

function containerLineLabel(c: NonNullable<BookingDetail["bookingContainers"]>[number]) {
  const t = c.containerType;
  if (t?.label) return t.label;
  if (t?.sizeFt) return `${t.sizeFt}ft${t.isReefer ? " Reefer" : ""}`;
  return t?.code ?? "Container";
}

/**
 * The real per-line container data lives on `bookingContainers` (joined
 * relation, with per-unit numbers + VGM) — `booking.containers` is a
 * frontend-only DTO shape the create/update flows use that the detail
 * endpoint never populates, so it's kept only as a last-resort fallback.
 */
export function containerSummary(b: BookingDetail) {
  const lines = b.bookingContainers ?? [];
  if (lines.length > 0) {
    return lines.map((c) => `${c.quantity} × ${containerLineLabel(c)}`).join(", ");
  }
  if (b.containers?.length) {
    return b.containers.map((c) => `${c.qty} × ${c.type}`).join(", ");
  }
  return b.freightType === "BULK" ? "Bulk cargo" : "—";
}

/**
 * Total shipped weight (VGM), in tons. Container bookings compute the real
 * total from `bookingContainers[].totalVgmTons` (per-line quantity × VGM)
 * because `cargoTotalWeightVgm` is often left at 0 for container freight —
 * the VGM is captured per container, not as a single booking-level figure.
 * Falls back to `cargoTotalWeightVgm` for bulk freight / legacy rows.
 */
export function totalVgmTons(b: BookingDetail): number {
  const lines = b.bookingContainers ?? [];
  if (lines.length > 0) {
    const sum = lines.reduce((s, c) => s + Number(c.totalVgmTons || 0), 0);
    if (sum > 0) return sum;
  }
  if (b.containers?.length) {
    const sum = b.containers.reduce(
      (s, c) => s + Number(c.vgm || 0) * Number(c.qty || 0),
      0,
    );
    if (sum > 0) return sum;
  }
  return Number(b.cargoTotalWeightVgm || 0);
}

export function fmtWeight(tons: number): string {
  return tons > 0 ? `${tons.toLocaleString(undefined, { maximumFractionDigits: 3 })} t` : "—";
}

/**
 * `booking.serviceType` is declared as the legacy "RAIL_ONLY" |
 * "RAIL_AND_FORWARDING" string on the shared type, but the API sends the
 * joined ServiceType relation object. Handle both shapes, with a fallback
 * derived from the first/last-mile addresses when neither is present.
 */
export function serviceTypeLabel(b: BookingDetail): string {
  const st = rawServiceType(b);
  if (st && typeof st === "object") {
    if (st.serviceName) return st.serviceName;
    if (st.code) {
      return st.code
        .replace(/_/g, " ")
        .toLowerCase()
        .replace(/\b\w/g, (m) => m.toUpperCase());
    }
  }
  if (typeof st === "string") {
    return st === "RAIL_AND_FORWARDING" ? "Rail + Forwarding" : "Rail only";
  }
  return b.firstMilePickupAddress || b.lastMileDeliveryAddress
    ? "Rail + Forwarding"
    : "Rail only";
}

/** Real commodity name from the joined cargo type, falling back to the
 *  free-text commodity entered at booking time. `freightSubtype` is a
 *  legacy field the API no longer sends. */
export function commodityLabel(b: BookingDetail): string {
  return (
    b.cargoType?.cargoTypeName ||
    b.cargoFreeText ||
    b.freightSubtype ||
    "—"
  );
}

export function shippingLineLabel(b: BookingDetail): string {
  return b.shippingLine?.label || b.shippingLine?.code || "—";
}

export function bookingSubtitle(b: BookingDetail) {
  const cargo =
    commodityLabel(b) !== "—"
      ? commodityLabel(b)
      : b.freightType === "BULK"
        ? "Bulk freight"
        : "Container freight";
  const load = containerSummary(b);
  const route = `${yardLabel(b.originYard)} → ${yardLabel(b.destinationYard)}`;
  return [cargo, load, route].filter((p) => p && p !== "—").join("  ·  ");
}

// ─── Pricing helpers ──────────────────────────────────────────────────────────

export type Pricing = Freight.PricingBreakdown | null | undefined;

export function priceLineItems(pricing: Pricing) {
  return (pricing?.lineItems ?? []).map((li) => ({
    label: li.description,
    value: `${li.amount.toLocaleString()} ${li.currency}`,
  }));
}

export function priceTotal(pricing: Pricing) {
  if (!pricing) return "—";
  const total = pricing.lineItems.reduce((s, li) => s + li.amount, 0);
  return `${total.toLocaleString()} ${pricing.currency}`;
}
