import { useState, useCallback, useMemo } from 'react';
import type { ComponentType } from 'react';
import { useTranslation } from 'react-i18next';
import { CarIcon, BicycleIcon, WalkingIcon, TransitIcon } from '../components/ui/icons';
import { dedupeTravelTimeEntries } from '../lib/travel-params';

export type TransportMode = 'car' | 'bicycle' | 'walking' | 'transit';

export const TRANSPORT_MODES: TransportMode[] = ['transit', 'car', 'bicycle', 'walking'];

export const MODE_LABELS: Record<TransportMode, string> = {
  car: 'Car',
  bicycle: 'Bicycle',
  walking: 'Walking',
  transit: 'Public Transport',
};

export const MODE_DESCRIPTIONS: Record<TransportMode, string> = {
  car: 'Drive time via the fastest road route',
  bicycle: 'Cycling time using bike-friendly routes',
  walking: 'Walking time along pedestrian paths and pavements',
  transit: 'Journey time by train, tube, and bus',
};

export const MODE_ICONS: Record<TransportMode, ComponentType<{ className?: string }>> = {
  car: CarIcon,
  bicycle: BicycleIcon,
  walking: WalkingIcon,
  transit: TransitIcon,
};

/**
 * Hook returning translated mode labels and descriptions.
 */
export function useTranslatedModes() {
  const { t } = useTranslation();
  const label = useCallback(
    (mode: TransportMode): string =>
      ({
        car: t('travel.modeCar'),
        bicycle: t('travel.modeBicycle'),
        walking: t('travel.modeWalking'),
        transit: t('travel.modeTransit'),
      })[mode],
    [t]
  );
  const desc = useCallback(
    (mode: TransportMode): string =>
      ({
        car: t('travel.modeCarDesc'),
        bicycle: t('travel.modeBicycleDesc'),
        walking: t('travel.modeWalkingDesc'),
        transit: t('travel.modeTransitDesc'),
      })[mode],
    [t]
  );
  return { label, desc };
}

export interface TravelTimeEntry {
  mode: TransportMode;
  slug: string;
  label: string;
  timeRange: [number, number] | null;
  /** Use best-case (5th percentile) travel time instead of median. Transit only. */
  useBest: boolean;
  /** Restrict transit to walk-transit-walk (0 changes). Optional; defaults to false. */
  noChange?: boolean;
  /** Restrict transit to at most one change. Optional; defaults to false.
   *  Mutually exclusive with noChange (the UI never sets both). */
  oneChange?: boolean;
  /** Drop buses from the allowed transit modes. Optional; defaults to false. */
  noBuses?: boolean;
}

/**
 * The Java pipeline emits 6 transit variants as separate parquet directories.
 * The UI represents transit with two toggle booleans; this maps the toggle
 * state back to the directory/mode name the server expects.
 *
 * For non-transit modes the entry.mode passes through unchanged.
 *
 * noChange and oneChange are mutually exclusive (the UI never sets both); if
 * they ever were, noChange wins here.
 */
export function resolveTransitVariant(entry: TravelTimeEntry): string {
  if (entry.mode !== 'transit') return entry.mode;
  const nc = entry.noChange ?? false;
  const oc = entry.oneChange ?? false;
  const nb = entry.noBuses ?? false;
  if (nc) return nb ? 'transit-no-change-no-bus' : 'transit-no-change';
  if (oc) return nb ? 'transit-one-change-no-bus' : 'transit-one-change';
  return nb ? 'transit-no-bus' : 'transit';
}

/**
 * Parse a server-side mode string (incl. transit variants) back into a base
 * TransportMode + UI toggle booleans. Returns null for mode strings the UI
 * cannot represent. Callers should skip entries that parse to null rather than
 * silently normalising to a different variant.
 */
export function parseServerMode(
  modeStr: string
): { mode: TransportMode; noChange: boolean; oneChange: boolean; noBuses: boolean } | null {
  if (modeStr === 'car' || modeStr === 'bicycle' || modeStr === 'walking') {
    return { mode: modeStr, noChange: false, oneChange: false, noBuses: false };
  }
  switch (modeStr) {
    case 'transit':
      return { mode: 'transit', noChange: false, oneChange: false, noBuses: false };
    case 'transit-no-bus':
      return { mode: 'transit', noChange: false, oneChange: false, noBuses: true };
    case 'transit-no-change':
      return { mode: 'transit', noChange: true, oneChange: false, noBuses: false };
    case 'transit-no-change-no-bus':
      return { mode: 'transit', noChange: true, oneChange: false, noBuses: true };
    case 'transit-one-change':
      return { mode: 'transit', noChange: false, oneChange: true, noBuses: false };
    case 'transit-one-change-no-bus':
      return { mode: 'transit', noChange: false, oneChange: true, noBuses: true };
    default:
      return null;
  }
}

/** Field key matching the backend response: tt_{server-mode}_{slug} */
export function travelFieldKey(entry: TravelTimeEntry): string {
  return `tt_${resolveTransitVariant(entry)}_${entry.slug}`;
}

/** Slider/data ceiling (minutes). Mirrors MAX_TRIP_DURATION_MINUTES in the R5 pipeline. */
export const MAX_TRAVEL_MINUTES = 90;

export interface TravelTimeInitial {
  entries?: TravelTimeEntry[];
}

export function useTravelTime(initial?: TravelTimeInitial) {
  const [entries, setEntries] = useState<TravelTimeEntry[]>(() =>
    dedupeTravelTimeEntries(initial?.entries ?? [])
  );

  const handleAddEntry = useCallback((mode: TransportMode) => {
    setEntries((prev) => [
      ...prev,
      {
        mode,
        slug: '',
        label: '',
        timeRange: null,
        useBest: false,
        noChange: false,
        oneChange: false,
        noBuses: false,
      },
    ]);
  }, []);

  const handleRemoveEntry = useCallback((index: number) => {
    setEntries((prev) => prev.filter((_, i) => i !== index));
  }, []);

  const handleSetDestination = useCallback((index: number, slug: string, label: string) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) =>
          i === index
            ? { ...entry, slug, label, timeRange: slug ? [0, MAX_TRAVEL_MINUTES] : null }
            : entry
        )
      )
    );
  }, []);

  const handleTimeRangeChange = useCallback((index: number, range: [number, number]) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) => (i === index ? { ...entry, timeRange: range } : entry))
      )
    );
  }, []);

  const handleToggleBest = useCallback((index: number) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) => (i === index ? { ...entry, useBest: !entry.useBest } : entry))
      )
    );
  }, []);

  // noChange and oneChange are mutually exclusive: enabling one clears the
  // other so the resolved server variant is never ambiguous.
  const handleToggleNoChange = useCallback((index: number) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) =>
          i === index ? { ...entry, noChange: !(entry.noChange ?? false), oneChange: false } : entry
        )
      )
    );
  }, []);

  const handleToggleOneChange = useCallback((index: number) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) =>
          i === index
            ? { ...entry, oneChange: !(entry.oneChange ?? false), noChange: false }
            : entry
        )
      )
    );
  }, []);

  const handleToggleNoBuses = useCallback((index: number) => {
    setEntries((prev) =>
      dedupeTravelTimeEntries(
        prev.map((entry, i) =>
          i === index ? { ...entry, noBuses: !(entry.noBuses ?? false) } : entry
        )
      )
    );
  }, []);

  const handleSetEntries = useCallback((newEntries: TravelTimeEntry[]) => {
    setEntries(dedupeTravelTimeEntries(newEntries));
  }, []);

  /** Entries that have a destination selected (slug is set) */
  const activeEntries = useMemo(() => entries.filter((e) => e.slug !== ''), [entries]);

  return {
    entries,
    activeEntries,
    handleAddEntry,
    handleRemoveEntry,
    handleSetDestination,
    handleSetEntries,
    handleTimeRangeChange,
    handleToggleBest,
    handleToggleNoChange,
    handleToggleOneChange,
    handleToggleNoBuses,
  };
}
