import type { Freight } from "@edr/types";
import { Box, Skeleton, Stack, Text } from "@mantine/core";
import { DatePickerInput } from "@mantine/dates";
import { CalendarDays, MapPin, Route as RouteIcon } from "lucide-react";
import { useCallback, useEffect, useMemo } from "react";
import { Controller, type UseFormReturn } from "react-hook-form";
import {
  BookingFormInputValues,
  type BookingFormValues,
  getRouteDirection,
} from "./schema";
import { SelectField, StepCard, StepHeader, StepLabel } from "./shared";

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

export function Step4Route({
  form,
  referenceData,
  isLoading,
  availableDays,
  noWagonForSelectedDay,
}: {
  form: BookingForm;
  referenceData?: Freight.BookingReferenceData;
  isLoading?: boolean;
  /**
   * Days with a departure whose train can carry the selected cargo type
   * (wagon-TYPE gate). `undefined` = unknown (loading/failed) — no gating.
   * Never carries capacity counts.
   */
  availableDays?: string[];
  noWagonForSelectedDay?: boolean;
}) {
  const originYard = form.watch("originYard");
  const destinationYard = form.watch("destinationYard");
  const operationType = form.watch("operationType");
  const isGeneralContract = form.watch("bookingType") === "general_contract";

  // The operation chosen in step 0 fixes which end of the route is inside
  // Ethiopia. Djibouti yards stand in for "outside Ethiopia" (the port),
  // mirroring getRouteDirection / the backend's deriveTradeDirection:
  //   import  → origin outside (Djibouti), destination Ethiopia
  //   export  → origin Ethiopia, destination outside (Djibouti)
  //   intercity → both Ethiopia (domestic)
  // _ff variants share the trade direction of their base operation.
  const { originCountry, destinationCountry } = useMemo(() => {
    switch (operationType) {
      case "import":
      case "import_ff":
        return { originCountry: "Djibouti", destinationCountry: "Ethiopia" };
      case "export":
      case "export_ff":
        return { originCountry: "Ethiopia", destinationCountry: "Djibouti" };
      case "intercity":
        return { originCountry: "Ethiopia", destinationCountry: "Ethiopia" };
      default:
        return { originCountry: null, destinationCountry: null };
    }
  }, [operationType]);

  const yardOptions = useMemo(() => {
    if (!referenceData?.yard) return [];
    return referenceData.yard.map((y) => ({ value: y.id, label: y.name }));
  }, [referenceData]);

  // Filter the yard list to one side of a route: the yards in `country` (the
  // operation type fixes which country each end must be in — see originCountry /
  // destinationCountry above), excluding the yard already chosen on the other
  // end of the same route so origin and destination can never match.
  const yardsForSide = useCallback(
    (country: string | null, excludeYardId: string) =>
      yardOptions
        .filter((o) => o.value !== excludeYardId)
        .filter((o) => {
          if (!country) return true;
          const yard = referenceData?.yard.find((y) => y.id === o.value);
          return yard?.country === country;
        }),
    [yardOptions, referenceData],
  );

  const originData = useMemo(
    () => yardsForSide(originCountry, destinationYard),
    [yardsForSide, originCountry, destinationYard],
  );
  const destData = useMemo(
    () => yardsForSide(destinationCountry, originYard),
    [yardsForSide, destinationCountry, originYard],
  );

  const origin = referenceData?.yard.find((y) => y.id === originYard);
  const dest = referenceData?.yard.find((y) => y.id === destinationYard);
  const direction = getRouteDirection(origin, dest);

  // Changing the operation type (step 0) can invalidate a yard already chosen
  // here — e.g. switching import→export flips which end must be in Ethiopia.
  // Clear any selection that no longer matches the operation's required country
  // so the customer can't submit a route that contradicts the operation.
  // Skipped right after a hydration `form.reset()` (draft restore / renewal
  // prefill) — a restored yard is trusted as-is until the customer changes
  // something live; `isDirty` is false only in that pristine post-reset render.
  useEffect(() => {
    if (!form.formState.isDirty) return;
    if (originCountry && origin && origin.country !== originCountry) {
      form.setValue("originYard", "");
    }
  }, [originCountry, origin, form]);
  useEffect(() => {
    if (!form.formState.isDirty) return;
    if (destinationCountry && dest && dest.country !== destinationCountry) {
      form.setValue("destinationYard", "");
    }
  }, [destinationCountry, dest, form]);

  const directionStyle: Record<string, string> = {
    EXPORT: "bg-sky-50 text-sky-800 border-sky-200",
    IMPORT: "bg-amber-50 text-amber-800 border-amber-200",
    DOMESTIC: "bg-gray-100 text-gray-600 border-gray-200",
  };
  const directionLabel: Record<string, string> = {
    EXPORT: "Export workflow (inside country to outside country)",
    IMPORT: "Import workflow (outside country to inside country)",
    DOMESTIC: "Domestic corridor",
  };

  const stationSelectDisabled = yardOptions.length === 0;

  // A general contract covers exactly ONE route — the same single
  // origin/destination pair as a one-time booking. (Multi-route contracts were
  // dropped; the multi-lane concept lives on the contracts module, not on
  // bookings.) The contracted quantity comes from the cargo step.

  // Earliest selectable shipment date (today, local) for the date input's `min`.
  const todayISODate = useMemo(() => {
    const now = new Date();
    const tz = now.getTimezoneOffset() * 60000;
    return new Date(now.getTime() - tz).toISOString().slice(0, 10);
  }, []);

  return (
    <StepCard>
      <StepHeader
        icon={<RouteIcon size={22} />}
        title="Route"
        description="Choose the origin and destination yards for your shipment."
      />

      {isLoading ? (
        <LoadingSkeleton />
      ) : (
        <div className="space-y-3">
          <StepLabel>Route</StepLabel>
          <div className="grid gap-4 sm:grid-cols-2">
            <Controller
              name="originYard"
              control={form.control}
              render={({ field, fieldState }) => (
                <SelectField
                  field={field}
                  error={fieldState.error}
                  label="Origin Yard *"
                  placeholder="Select origin..."
                  disabled={stationSelectDisabled}
                  data={originData}
                />
              )}
            />
            <Controller
              name="destinationYard"
              control={form.control}
              render={({ field, fieldState }) => (
                <SelectField
                  field={field}
                  error={fieldState.error}
                  label="Destination Yard *"
                  placeholder="Select destination..."
                  disabled={stationSelectDisabled}
                  data={destData}
                />
              )}
            />
          </div>
          {direction && (
            <div
              className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium ${directionStyle[direction]}`}
            >
              <MapPin className="h-3.5 w-3.5 shrink-0" />
              {directionLabel[direction]}
            </div>
          )}
          {/* Estimated shipment date — one-time bookings only. General contracts
              pick the date per order drawn against the contract later. */}
          {!isGeneralContract && (
            <Box w="100%">
              <Controller
                name="scheduledDate"
                control={form.control}
                render={({ field, fieldState }) => (
                  <DatePickerInput
                    w="100%"
                    label="Estimated shipment date *"
                    description="A planning estimate. You'll confirm the actual date when you request the operation."
                    placeholder="Pick a date"
                    minDate={todayISODate}
                    leftSection={<CalendarDays size={16} />}
                    error={fieldState.error?.message}
                    // Days whose trains cannot carry the selected cargo type are
                    // not selectable (wagon-TYPE gate; quantity never blocks).
                    excludeDate={(date) => {
                      if (!availableDays) return false;
                      const day =
                        typeof date === "string"
                          ? date.slice(0, 10)
                          : new Date(date).toISOString().slice(0, 10);
                      return !availableDays.includes(day);
                    }}
                    // Mantine v9 DatePickerInput uses string (YYYY-MM-DD) values,
                    // matching the form's `scheduledDate` string directly.
                    value={field.value || null}
                    onChange={(v) => field.onChange(v ?? "")}
                    onBlur={field.onBlur}
                    radius="md"
                    popoverProps={{ withinPortal: true }}
                  />
                )}
              />
              {noWagonForSelectedDay && (
                <Text size="xs" c="red.7" mt={6}>
                  No wagon on this day&apos;s train can carry your cargo type —
                  please pick another available day.
                </Text>
              )}
              {availableDays && availableDays.length === 0 && (
                <Text size="xs" c="red.7" mt={6}>
                  No upcoming departure can carry this cargo type on the chosen
                  route right now. Try a different cargo/container type or check
                  back later.
                </Text>
              )}
            </Box>
          )}
        </div>
      )}

    </StepCard>
  );
}

function LoadingSkeleton() {
  return (
    <div className="space-y-4 rounded-xl border border-gray-200 p-4">
      <div className="grid gap-3 sm:grid-cols-2">
        <Stack gap={8}>
          <Skeleton height={12} w={80} radius="sm" />
          <Skeleton height={40} radius="md" />
        </Stack>
        <Stack gap={8}>
          <Skeleton height={12} w={96} radius="sm" />
          <Skeleton height={40} radius="md" />
        </Stack>
      </div>
      <Skeleton height={32} radius="md" />
    </div>
  );
}
