import { useEffect, useMemo, useRef, type KeyboardEvent } from "react";
import { Controller, useFieldArray, type UseFormReturn } from "react-hook-form";
import { Flame, Package, Plus, Snowflake, Trash2, Weight } from "lucide-react";
import { ActionIcon, Button, Skeleton, Text, TextInput } from "@mantine/core";
import type { Freight } from "@edr/types";
import {
  BookingFormInputValues,
  calcWagons,
  type BookingFormValues,
} from "./schema";
import {
  AlertBox,
  fieldStyles,
  OptionCard,
  OptionFieldError,
  SelectField,
  StepCard,
  StepHeader,
  StepLabel,
  ToggleRow,
  UnitCountToggles,
} from "./shared";

type BookingForm = UseFormReturn<
  BookingFormInputValues,
  any,
  BookingFormValues
>;

/**
 * Every quantity on this step is a non-negative magnitude. A native number
 * input's `min` only constrains its stepper, so swallow the minus key before it
 * can put a negative into the field at all.
 */
const blockNegative = (event: KeyboardEvent<HTMLInputElement>) => {
  if (event.key === "-") event.preventDefault();
};

export function Step5CargoDetails({
  form,
  referenceData,
  isLoading,
}: {
  form: BookingForm;
  referenceData?: Freight.BookingReferenceData;
  isLoading?: boolean;
}) {
  const cargoType = form.watch("cargoType");
  const cargoTypePath = form.watch("cargoTypePath") ?? [];
  const parentId = cargoTypePath[0];
  const childId = cargoTypePath[1];
  const containers = form.watch("containers");
  // General contracts reserve quantity per route, captured in the route step
  // against each route. So the single cargo-level quantity below is only asked
  // for one-time bookings; contracts skip it here to avoid a duplicate input.
  const isGeneralContract = form.watch("bookingType") === "general_contract";

  const { fields, append, remove } = useFieldArray({
    control: form.control,
    name: "containers",
  });

  const containerTypeOptionsBySize = useMemo(() => {
    if (!referenceData?.containers) return new Map<string, string[]>();
    return new Map(
      referenceData.containers.map((g) => [g.size, g.types.map((t) => t.name)]),
    );
  }, [referenceData]);

  // Reset the chosen commodity ONLY when the parent group actually changes to a
  // different one. The previous version reset on every render where parentId was
  // truthy, which wiped a valid commodity whenever the step re-rendered or was
  // revisited (e.g. navigating Back/Next or restoring a saved draft) — making the
  // commodity selection appear not to stick. Tracking the previous parent lets us
  // clear the child on a real parent switch while leaving an existing selection
  // intact on mount/re-render.
  const prevParentIdRef = useRef<string | undefined>(parentId);
  useEffect(() => {
    if (prevParentIdRef.current === parentId) return;
    const switchedToAnotherParent =
      !!prevParentIdRef.current && !!parentId;
    prevParentIdRef.current = parentId;
    if (switchedToAnotherParent) {
      form.setValue("cargoTypePath", [parentId as string, ""], {
        shouldDirty: true,
      });
    }
  }, [parentId, form]);

  const selectedCommodity = useMemo(() => {
    if (!referenceData?.cargo_type || !parentId || !childId) return null;
    const group = referenceData.cargo_type.find((g) => g.id === parentId);
    return group?.children?.find((c) => c.id === childId) ?? null;
  }, [referenceData, parentId, childId]);

  // Unit of measure for bulk/break-bulk cargo: PER_ITEM → ask for a total item
  // count; otherwise ask for an estimated tonnage. Drives the quantity field's
  // label, icon, and step so customers enter the right unit.
  const isPerItem = selectedCommodity?.unit_of_measure === "PER_ITEM";

  const freightTypeGroups = useMemo(() => {
    if (!referenceData?.cargo_type) return [];
    return referenceData.cargo_type.filter(
      (g) => g.code !== "CONTAINER" && !/container/i.test(g.name),
    );
  }, [referenceData]);

  const freightTypeOptions = useMemo(
    () =>
      freightTypeGroups.map((g) => ({
        value: g.id,
        label: g.name,
      })),
    [freightTypeGroups],
  );

  const commodityOptions = useMemo(() => {
    if (!referenceData?.cargo_type || !parentId) return [];
    const group = referenceData.cargo_type.find((g) => g.id === parentId);
    return (
      group?.children?.map((c) => ({
        value: c.id,
        label: c.name,
      })) ?? []
    );
  }, [referenceData, parentId]);

  // ── Bulk hazmat/reefer (whole-cargo portion) helpers ──────────────────────
  // The bulk amounts are in the commodity's own unit and bounded by cargoWeight.
  const bulkUnitLabel = isPerItem ? "items" : "tons";
  const bulkCargoQty = () =>
    Math.max(0, Number(form.getValues("cargoWeight") ?? 0) || 0);
  const bulkMax = () => bulkCargoQty() || undefined;
  // Default a switched-on portion to the whole cargo amount.
  const bulkDefaultQty = () => {
    const q = bulkCargoQty();
    return q > 0 ? String(q) : "1";
  };
  // Clamp a typed value into 0..cargoWeight (items round down). Empty stays empty
  // so the field can be cleared; the schema then flags it as required.
  const clampToBulk = (raw: string) => {
    if (raw === "") return "";
    const n = Number(raw);
    if (Number.isNaN(n)) return raw;
    const cap = bulkCargoQty();
    const stepped = isPerItem ? Math.floor(n) : n;
    const bounded = cap > 0 ? Math.min(cap, stepped) : stepped;
    return Math.max(0, bounded).toString();
  };

  // ── Per-line hazmat/reefer quantity helpers ───────────────────────────────
  // The line quantity (number of containers in this line) is the upper bound for
  // both the hazardous and the refrigerated counts.
  const lineQtyOf = (index: number) =>
    Math.max(1, Number(form.getValues(`containers.${index}.qty`) ?? 1) || 1);
  const lineMax = (index: number) => lineQtyOf(index);
  // After the line quantity changes, pull any active count back within bounds.
  const clampDependentQty = (index: number, newLineQty: number) => {
    const max = Math.max(1, newLineQty);
    (["hazardousQty", "reeferQty"] as const).forEach((key) => {
      const cur = Number(form.getValues(`containers.${index}.${key}`) ?? 0);
      if (cur > max) {
        form.setValue(`containers.${index}.${key}`, max.toString(), {
          shouldDirty: true,
          shouldValidate: true,
        });
      }
    });
  };

  if (isLoading) {
    return (
      <StepCard>
        <StepHeader
          icon={<Package size={22} />}
          title="Cargo Details"
          description="Choose your cargo type and configuration."
        />
        <div className="space-y-4">
          <Skeleton height={14} w={96} radius="sm" />
          <div className="grid gap-4 sm:grid-cols-2">
            <Skeleton height={96} radius="lg" />
            <Skeleton height={96} radius="lg" />
          </div>
          <Skeleton height={44} radius="md" />
          <Skeleton height={44} w="33%" radius="md" />
        </div>
      </StepCard>
    );
  }

  return (
    <StepCard>
      <StepHeader
        icon={<Package size={22} />}
        title="Cargo Details"
        description="Choose your cargo type and configuration. Container weight is captured later in operations."
      />

      {/* Cargo Type */}
      <div className="space-y-3">
        <StepLabel>Cargo Type *</StepLabel>
        <Controller
          name="cargoType"
          control={form.control}
          render={({ field, fieldState }) => (
            <div>
              <div className="grid gap-4 sm:grid-cols-2">
                <OptionCard
                  selected={cargoType === "container"}
                  icon={<Package className="h-5 w-5" />}
                  iconBg="#ECF6F1"
                  iconColor="#0A6F4D"
                  title="Containerized"
                  description="Pre-packed containerized cargo (20ft / 40ft)."
                  onClick={() => {
                    field.onChange("container");
                    form.setValue("cargoTypePath", [], { shouldDirty: true });
                  }}
                />
                <OptionCard
                  selected={cargoType === "bulk"}
                  icon={<Weight className="h-5 w-5" />}
                  iconBg="#FDF3E0"
                  iconColor="#C77F09"
                  title="General Cargo"
                  description="Bulk commodities or break-bulk cargo."
                  onClick={() => {
                    field.onChange("bulk");
                    form.setValue("containers", [], { shouldDirty: true });
                  }}
                />
              </div>
              <OptionFieldError error={fieldState.error} />
            </div>
          )}
        />
      </div>

      {/* Bulk freight type — pick the commodity FIRST so we know whether the
          cargo is measured in tons or items before asking for the quantity. */}
      {cargoType === "bulk" && (
        <div className="space-y-3">
          {freightTypeOptions.length > 0 ? (
            <Controller
              name="cargoTypePath.0"
              control={form.control}
              render={({ field, fieldState }) => (
                <SelectField
                  field={field}
                  error={fieldState.error}
                  label="Bulk Cargo Type *"
                  placeholder="Select freight type..."
                  data={freightTypeOptions}
                />
              )}
            />
          ) : (
            <Text size="sm" c="dimmed">
              No freight types available.
            </Text>
          )}

          {parentId && commodityOptions.length > 0 && (
            <Controller
              name="cargoTypePath.1"
              control={form.control}
              render={({ field, fieldState }) => (
                <SelectField
                  field={field}
                  error={fieldState.error}
                  label="Commodity *"
                  placeholder="Select type..."
                  data={commodityOptions}
                />
              )}
            />
          )}

          {selectedCommodity && (
            <Controller
              name="cargoFreeText"
              control={form.control}
              render={({ field, fieldState }) => (
                <TextInput
                  {...field}
                  label="Describe cargo *"
                  placeholder="e.g. Charcoal, Wheat, etc."
                  error={fieldState.error?.message}
                  radius="md"
                />
              )}
            />
          )}

          {/* Quantity — only once a commodity is chosen, so the unit (tons vs
              items) is known. PER_TON asks for estimated tons; PER_ITEM asks
              for the total item count. General contracts collect this per route
              in the route step instead, so it's hidden here for them. */}
          {selectedCommodity && !isGeneralContract && (
            <Controller
              name="cargoWeight"
              control={form.control}
              render={({ field, fieldState }) => (
                <TextInput
                  {...field}
                  onChange={(e) => {
                    field.onChange(e);
                    // Pull any active bulk hazmat/reefer portion back within the
                    // new cargo amount so it can't outlive a shrink.
                    const cap = Number(e.currentTarget.value);
                    if (!Number.isNaN(cap)) {
                      (["bulkHazardousQty", "bulkReeferQty"] as const).forEach(
                        (key) => {
                          const cur = Number(form.getValues(key) ?? 0);
                          if (cap > 0 && cur > cap) {
                            form.setValue(key, String(cap), {
                              shouldDirty: true,
                              shouldValidate: true,
                            });
                          }
                        },
                      );
                    }
                  }}
                  id="cargoWeight"
                  type="number"
                  onKeyDown={blockNegative}
                  label={isPerItem ? "Quantity (Items) *" : "Quantity (Tons) *"}
                  placeholder={isPerItem ? "e.g. 500" : "e.g. 1200"}
                  leftSection={
                    isPerItem ? (
                      <Package className="h-4 w-4" />
                    ) : (
                      <Weight className="h-4 w-4" />
                    )
                  }
                  error={fieldState.error?.message}
                  description={
                    isPerItem
                      ? "Total count of items you plan to import or export."
                      : "Estimated total tonnage to ship."
                  }
                  radius={10}
                  styles={fieldStyles}
                  min={0}
                  step={isPerItem ? 1 : 0.01}
                />
              )}
            />
          )}

          {/* Cargo handling — how much of the cargo is hazardous / refrigerated,
              in the SAME unit as the quantity above (tons or items). Shown once a
              commodity is chosen so the unit is known; general contracts handle
              handling per order. */}
          {selectedCommodity && !isGeneralContract && (
            <div className="grid gap-3 sm:grid-cols-2">
              <Controller
                name="isHazardous"
                control={form.control}
                render={({ field: hazField }) => (
                  <ToggleRow
                    icon={<Flame size={18} />}
                    iconBg="#FBEAE7"
                    iconColor="#C0392B"
                    title="Hazardous"
                    description={`Part of this cargo is hazardous (in ${bulkUnitLabel}).`}
                    checked={!!hazField.value}
                    onChange={(v) => {
                      hazField.onChange(v);
                      form.setValue(
                        "bulkHazardousQty",
                        v ? bulkDefaultQty() : "0",
                        { shouldDirty: true, shouldValidate: true },
                      );
                    }}
                  >
                    <Controller
                      name="bulkHazardousQty"
                      control={form.control}
                      render={({ field: hq, fieldState }) => (
                        <TextInput
                          type="number"
                          onKeyDown={blockNegative}
                          size="sm"
                          label={
                            isPerItem
                              ? "How many items are hazardous?"
                              : "How many tons are hazardous?"
                          }
                          min={isPerItem ? 1 : 0}
                          max={bulkMax()}
                          step={isPerItem ? 1 : 0.01}
                          value={hq.value ?? ""}
                          onChange={(e) =>
                            hq.onChange(clampToBulk(e.currentTarget.value))
                          }
                          onBlur={hq.onBlur}
                          error={fieldState.error?.message}
                          radius="md"
                        />
                      )}
                    />
                  </ToggleRow>
                )}
              />
              <Controller
                name="isRefrigerated"
                control={form.control}
                render={({ field: reeField }) => (
                  <ToggleRow
                    icon={<Snowflake size={18} />}
                    iconBg="#E9F0F8"
                    iconColor="#2E5B96"
                    title="Refrigerated"
                    description={`Part of this cargo needs reefer transport (in ${bulkUnitLabel}).`}
                    checked={!!reeField.value}
                    onChange={(v) => {
                      reeField.onChange(v);
                      form.setValue(
                        "bulkReeferQty",
                        v ? bulkDefaultQty() : "0",
                        { shouldDirty: true, shouldValidate: true },
                      );
                    }}
                  >
                    <Controller
                      name="bulkReeferQty"
                      control={form.control}
                      render={({ field: rq, fieldState }) => (
                        <TextInput
                          type="number"
                          onKeyDown={blockNegative}
                          size="sm"
                          label={
                            isPerItem
                              ? "How many items are refrigerated?"
                              : "How many tons are refrigerated?"
                          }
                          min={isPerItem ? 1 : 0}
                          max={bulkMax()}
                          step={isPerItem ? 1 : 0.01}
                          value={rq.value ?? ""}
                          onChange={(e) =>
                            rq.onChange(clampToBulk(e.currentTarget.value))
                          }
                          onBlur={rq.onBlur}
                          error={fieldState.error?.message}
                          radius="md"
                        />
                      )}
                    />
                  </ToggleRow>
                )}
              />
            </div>
          )}
        </div>
      )}

      {/* Container list — type + quantity only; no weight is collected here. */}
      {cargoType === "container" && (
        <>
          <div className="space-y-4">
            <div className="flex items-center justify-between">
              <StepLabel>Containers</StepLabel>
              <Button
                type="button"
                variant="default"
                size="sm"
                radius="md"
                leftSection={<Plus size={14} />}
                onClick={() =>
                  append({
                    type: "20ft",
                    containerType: "",
                    qty: "1",
                    vgm: "0",
                  })
                }
              >
                Add Container
              </Button>
            </div>

            {fields.map((field, index) => (
              <div
                key={field.id}
                className="flex flex-col gap-3 rounded-xl border border-gray-200 bg-gray-50/50 p-4"
              >
                <div className="flex items-center justify-between">
                  <Text
                    size="xs"
                    fw={600}
                    c="dimmed"
                    tt="uppercase"
                    className="tracking-wide"
                  >
                    Container {index + 1}
                  </Text>
                  {fields.length > 1 && (
                    <ActionIcon
                      color="red"
                      variant="subtle"
                      size="sm"
                      onClick={() => remove(index)}
                      aria-label="Remove container"
                    >
                      <Trash2 size={15} />
                    </ActionIcon>
                  )}
                </div>

                {/* Container size */}
                <Controller
                  name={`containers.${index}.type`}
                  control={form.control}
                  render={({ field: typeField, fieldState }) => (
                    <div>
                      <div className="grid gap-2 sm:grid-cols-2">
                        {[
                          { val: "20ft" as const, label: "20ft Container (TEU)" },
                          { val: "40ft" as const, label: "40ft Container (FEU)" },
                        ].map((ct) => (
                          <OptionCard
                            key={ct.val}
                            selected={typeField.value === ct.val}
                            onClick={() => typeField.onChange(ct.val)}
                          >
                            <div className="flex items-center gap-2">
                              <Package className="h-4 w-4 text-emerald-600" />
                              <p className="font-semibold">{ct.label}</p>
                            </div>
                          </OptionCard>
                        ))}
                      </div>
                      <OptionFieldError error={fieldState.error} />
                    </div>
                  )}
                />

                {/* Quantity + Container Type */}
                <div className="grid gap-3 sm:grid-cols-2">
                  <Controller
                    name={`containers.${index}.qty`}
                    control={form.control}
                    render={({ field: qtyField, fieldState }) => {
                      // Keep the per-line hazmat/reefer quantities within the new
                      // line quantity whenever it drops, so an old larger value
                      // can't outlive a shrink.
                      const setQty = (next: number) => {
                        const n = Math.max(1, next);
                        qtyField.onChange(n.toString());
                        clampDependentQty(index, n);
                      };
                      return (
                        <div>
                          <Text size="sm" fw={500} mb={4}>
                            Quantity *
                          </Text>
                          <div className="flex items-center gap-2">
                            <button
                              type="button"
                              onClick={() =>
                                setQty(Number(qtyField.value ?? 1) - 1)
                              }
                              className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-white text-sm font-medium transition hover:bg-gray-50"
                            >
                              −
                            </button>
                            <input
                              value={qtyField.value ?? 1}
                              onChange={(e) => {
                                qtyField.onChange(e.target.value);
                                const n = Number(e.target.value);
                                if (!Number.isNaN(n) && n >= 1)
                                  clampDependentQty(index, n);
                              }}
                              onBlur={qtyField.onBlur}
                              type="number"
                              onKeyDown={blockNegative}
                              min={1}
                              className="h-9 w-full rounded-lg border border-gray-200 bg-white px-3 text-center text-sm outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500"
                            />
                            <button
                              type="button"
                              onClick={() =>
                                setQty(Number(qtyField.value ?? 1) + 1)
                              }
                              className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-white text-sm font-medium transition hover:bg-gray-50"
                            >
                              +
                            </button>
                          </div>
                          {fieldState.error?.message && (
                            <Text size="xs" c="red" mt={4}>
                              {fieldState.error.message}
                            </Text>
                          )}
                        </div>
                      );
                    }}
                  />

                  <Controller
                    name={`containers.${index}.containerType`}
                    control={form.control}
                    render={({ field: ctField, fieldState }) => (
                      <SelectField
                        field={ctField}
                        error={fieldState.error}
                        label="Container Type *"
                        placeholder="Select type..."
                        data={
                          containerTypeOptionsBySize.get(
                            containers[index]?.type ?? "20ft",
                          ) ?? []
                        }
                      />
                    )}
                  />
                </div>

                {/* Per-container hazmat / reefer. Each switch reveals a bounded
                    "how many of this line" input (1..line quantity). */}
                <div className="grid gap-3 sm:grid-cols-2">
                  <Controller
                    name={`containers.${index}.isHazardous`}
                    control={form.control}
                    render={({ field: hazField }) => (
                      <ToggleRow
                        icon={<Flame size={18} />}
                        iconBg="#FBEAE7"
                        iconColor="#C0392B"
                        title="Hazardous"
                        description="Some of these containers carry hazardous cargo."
                        checked={!!hazField.value}
                        onChange={(v) => {
                          hazField.onChange(v);
                          form.setValue(
                            `containers.${index}.hazardousQty`,
                            v ? "1" : "0",
                            { shouldDirty: true, shouldValidate: true },
                          );
                        }}
                      >
                        <Controller
                          name={`containers.${index}.hazardousQty`}
                          control={form.control}
                          render={({ field: hq, fieldState }) => (
                            <div>
                              <UnitCountToggles
                                total={lineMax(index)}
                                value={hq.value ?? "0"}
                                onChange={hq.onChange}
                                label="Tap the hazardous containers"
                                activeBg="#FBEAE7"
                                activeBorder="#E4A69B"
                                activeColor="#C0392B"
                              />
                              {fieldState.error?.message ? (
                                <Text fz={11} c="red.7" mt={4}>
                                  {fieldState.error.message}
                                </Text>
                              ) : null}
                            </div>
                          )}
                        />
                      </ToggleRow>
                    )}
                  />
                  <Controller
                    name={`containers.${index}.isReefer`}
                    control={form.control}
                    render={({ field: reeField }) => (
                      <ToggleRow
                        icon={<Snowflake size={18} />}
                        iconBg="#E9F0F8"
                        iconColor="#2E5B96"
                        title="Refrigerated"
                        description="Some of these containers need reefer transport."
                        checked={!!reeField.value}
                        onChange={(v) => {
                          reeField.onChange(v);
                          form.setValue(
                            `containers.${index}.reeferQty`,
                            v ? "1" : "0",
                            { shouldDirty: true, shouldValidate: true },
                          );
                        }}
                      >
                        <Controller
                          name={`containers.${index}.reeferQty`}
                          control={form.control}
                          render={({ field: rq, fieldState }) => (
                            <div>
                              <UnitCountToggles
                                total={lineMax(index)}
                                value={rq.value ?? "0"}
                                onChange={rq.onChange}
                                label="Tap the refrigerated containers"
                                activeBg="#E9F0F8"
                                activeBorder="#A9C2E0"
                                activeColor="#2E5B96"
                              />
                              {fieldState.error?.message ? (
                                <Text fz={11} c="red.7" mt={4}>
                                  {fieldState.error.message}
                                </Text>
                              ) : null}
                            </div>
                          )}
                        />
                      </ToggleRow>
                    )}
                  />
                </div>
              </div>
            ))}
          </div>

          {(() => {
            const result = calcWagons(containers ?? []);
            if (result.hasOddUnit) {
              return (
                <AlertBox tone="error">
                  <p className="font-semibold">
                    Odd number of 20ft containers ({result.ft20Wagons})
                  </p>
                  <p className="mt-1 text-xs">
                    20ft containers travel two per wagon, so they must be booked in
                    even numbers. Please <strong>add one more 20ft container</strong>{" "}
                    or <strong>remove one</strong> (e.g. book{" "}
                    {result.ft20Wagons + 1} or {result.ft20Wagons - 1} instead of{" "}
                    {result.ft20Wagons}) — the booking cannot be submitted with an
                    unpaired 20ft container.
                  </p>
                </AlertBox>
              );
            }
            return null;
          })()}
        </>
      )}
    </StepCard>
  );
}
