import {
  useState,
  useEffect,
  useMemo,
  useRef,
  lazy,
  Suspense,
  type ComponentType,
  type MutableRefObject,
} from 'react';
import type { TFunction } from 'i18next';
import { useTranslation } from 'react-i18next';
import { cellToLatLng, polygonToCells } from 'h3-js';
import PriceHistoryChart from '../map/PriceHistoryChart';
import StackedBarChart from '../map/StackedBarChart';
import JourneyInstructions, { type JourneyInstructionPreset } from '../map/JourneyInstructions';
import { DualHistogram } from '../map/DualHistogram';
import { FeatureLabel } from '../ui/FeatureLabel';
import { Slider } from '../ui/Slider';
import {
  ChartBarIcon,
  CheckIcon,
  ClipboardIcon,
  DownloadIcon,
  FilterIcon,
  MapPinIcon,
  RouteIcon,
} from '../ui/icons';
import type { TravelTimeEntry } from '../../hooks/useTravelTime';
import { PARTY_FEATURE_COLORS, STACKED_SEGMENT_COLORS } from '../../lib/consts';
import { formatValue } from '../../lib/format';
import type {
  FeatureMeta,
  HexagonData,
  POI,
  PostcodeFeature,
  PricePoint,
  ViewChangeParams,
  ViewState,
} from '../../types';

const SHOWCASE_STEP_COUNT = 4;
const ProductMap = lazy(() => import('../map/Map'));
const SHOWCASE_INTERVAL_MS = 5200;
const SHOWCASE_SCOUT_INTERVAL_MS = 9000;
const SHOWCASE_STEP_INTERVALS_MS = [
  SHOWCASE_INTERVAL_MS,
  SHOWCASE_INTERVAL_MS,
  SHOWCASE_INTERVAL_MS,
  SHOWCASE_SCOUT_INTERVAL_MS,
];
const FILTER_ANIMATION_MS = 5000;
const INSPECT_SCROLL_ANIMATION_MS = 4600;
const SCOUT_TABLE_REVEAL_MS = 2400;
type ShowcaseStep = {
  tab: string;
  title: string;
  body: string;
  Icon: ComponentType<{ className?: string }>;
};

type ProductShowcaseProps = {
  className?: string;
};

const DEMO_FEATURES: FeatureMeta[] = [
  {
    name: 'Estimated price',
    type: 'numeric',
    group: 'Properties',
    min: 0,
    max: 900000,
    step: 25000,
    prefix: '£',
  },
  {
    name: 'Serious crime (avg/yr)',
    type: 'numeric',
    group: 'Crime',
    min: 0,
    max: 40,
    step: 1,
  },
  {
    name: 'Good+ primary school catchments',
    type: 'numeric',
    group: 'Schools',
    min: 0,
    max: 8,
    step: 1,
  },
  {
    name: 'Noise (dB)',
    type: 'numeric',
    group: 'Defining characteristics',
    min: 40,
    max: 80,
    step: 1,
    suffix: ' dB',
  },
  {
    name: 'Travel time to nearest train or tube station (min)',
    type: 'numeric',
    group: 'Transport',
    min: 0,
    max: 60,
    step: 1,
    suffix: ' min',
  },
];

const PRICE_POINTS: PricePoint[] = [
  { year: 2017.15, price: 358000 },
  { year: 2017, price: 365000 },
  { year: 2017.42, price: 382000 },
  { year: 2017.78, price: 354000 },
  { year: 2018.2, price: 378000 },
  { year: 2018, price: 372000 },
  { year: 2018.67, price: 392000 },
  { year: 2019.12, price: 383000 },
  { year: 2019, price: 389000 },
  { year: 2019.55, price: 401000 },
  { year: 2019.88, price: 415000 },
  { year: 2020.25, price: 395000 },
  { year: 2020, price: 402000 },
  { year: 2020.62, price: 423000 },
  { year: 2021.08, price: 421000 },
  { year: 2021, price: 431000 },
  { year: 2021.34, price: 446000 },
  { year: 2021.79, price: 437000 },
  { year: 2022.17, price: 449000 },
  { year: 2022, price: 456000 },
  { year: 2022.53, price: 467000 },
  { year: 2022.91, price: 463000 },
  { year: 2023.22, price: 472000 },
  { year: 2023, price: 470000 },
  { year: 2023.63, price: 483000 },
  { year: 2024.18, price: 474000 },
  { year: 2024, price: 482000 },
  { year: 2024.71, price: 493000 },
  { year: 2025, price: 492000 },
];

const SCHOOL_HISTOGRAM_GLOBAL = [12, 20, 27, 24, 18, 12, 7, 4, 2];
const SCHOOL_HISTOGRAM_LOCAL = [0, 0, 0, 0, 18, 0, 0, 0, 0];
const SCHOOL_NEARBY_COUNT = 4;
const SCHOOL_GLOBAL_MEAN = 2.6;
const CRIME_SEGMENTS = [
  { name: 'Anti-social', value: 34 },
  { name: 'Vehicle', value: 18 },
  { name: 'Burglary', value: 11 },
  { name: 'Other', value: 19 },
];

const VOTE_SHARE_SEGMENTS = [
  { name: '% Labour', value: 42 },
  { name: '% Conservative', value: 17 },
  { name: '% Liberal Democrat', value: 12 },
  { name: '% Reform UK', value: 7 },
  { name: '% Green', value: 18 },
  { name: '% Other parties', value: 4 },
];

const INSPECT_TRAVEL_ENTRIES: TravelTimeEntry[] = [
  {
    mode: 'transit',
    slug: 'oxford-circus',
    label: 'Oxford Circus',
    timeRange: [0, 45],
    useBest: false,
  },
];

const INSPECT_JOURNEYS: JourneyInstructionPreset[] = [
  {
    slug: 'oxford-circus',
    label: 'Oxford Circus',
    minutes: 34,
    bestMinutes: 29,
    legs: [
      { mode: 'Victoria', from: 'Oxford Circus Underground Station', to: 'Victoria', minutes: 4 },
      { mode: 'District', from: 'Victoria', to: "Earl's Court", minutes: 10 },
      { mode: 'walk', from: "Earl's Court", to: 'SW5 9AA', minutes: 7 },
    ],
  },
];

const ENGLAND_SHOWCASE_POLYGON: number[][] = [
  [49.95, -5.72],
  [50.16, -3.84],
  [50.5, -2.22],
  [50.74, -0.18],
  [51.12, 1.4],
  [52.04, 1.72],
  [53.02, 1.2],
  [54.12, 0.04],
  [55.58, -1.58],
  [55.82, -2.05],
  [54.96, -3.08],
  [54.34, -3.42],
  [53.45, -3.06],
  [52.76, -2.2],
  [52.02, -2.78],
  [51.38, -3.06],
  [50.78, -3.52],
  [50.22, -4.76],
];

const SHOWCASE_MAP_START_VIEW: ViewState = {
  longitude: -1.7,
  latitude: 52.7,
  zoom: 6.6,
  pitch: 0,
  bearing: 0,
};

const SHOWCASE_MAP_END_VIEW: ViewState = {
  longitude: -1.89,
  latitude: 52.49,
  zoom: 8.72,
  pitch: 0,
  bearing: 0,
};

const SHOWCASE_MAP_CENTRES = [
  { lat: 52.4862, lon: -1.8904, weight: 190, spread: 0.48 },
  { lat: 51.5072, lon: -0.1276, weight: 112, spread: 0.5 },
  { lat: 53.4808, lon: -2.2426, weight: 92, spread: 0.42 },
  { lat: 53.8008, lon: -1.5491, weight: 74, spread: 0.38 },
  { lat: 52.9548, lon: -1.1581, weight: 68, spread: 0.34 },
  { lat: 51.4545, lon: -2.5879, weight: 58, spread: 0.34 },
];

function stableMapJitter(value: string): number {
  let hash = 0;
  for (let index = 0; index < value.length; index += 1) {
    hash = (hash * 31 + value.charCodeAt(index)) % 9973;
  }
  return hash / 9973;
}

function buildShowcaseMapData(): HexagonData[] {
  return polygonToCells(ENGLAND_SHOWCASE_POLYGON, 6).flatMap((h3) => {
    const [lat, lon] = cellToLatLng(h3);
    const centreWeight = SHOWCASE_MAP_CENTRES.reduce((sum, centre) => {
      const latDelta = (lat - centre.lat) * 1.28;
      const lonDelta = (lon - centre.lon) * Math.cos((centre.lat * Math.PI) / 180);
      const distance = Math.hypot(latDelta, lonDelta);
      return sum + Math.max(0, 1 - distance / centre.spread) * centre.weight;
    }, 0);
    const corridorWeight = Math.max(0, 1 - Math.abs(lon + 1.7 + (lat - 52.45) * 0.34) / 0.62);
    const jitter = stableMapJitter(h3);
    const inclusionChance = Math.min(0.42, 0.04 + centreWeight / 340);
    if (centreWeight < 18 || jitter > inclusionChance) return [];

    const count = Math.round(18 + centreWeight + corridorWeight * 22 + jitter * 42);

    return { h3, lat, lon, count };
  });
}

const SHOWCASE_MAP_DATA = buildShowcaseMapData();
const SHOWCASE_MAP_TOTAL_COUNT = SHOWCASE_MAP_DATA.reduce((sum, item) => sum + item.count, 0);
const EMPTY_SHOWCASE_POSTCODES: PostcodeFeature[] = [];
const EMPTY_SHOWCASE_POIS: POI[] = [];

function noopViewChange(_params: ViewChangeParams) {
  void _params;
}

function noopHexagonClick(_id: string) {
  void _id;
}

function noopHexagonHover(_h3: string | null) {
  void _h3;
}

function easeInOutCubic(value: number): number {
  return value < 0.5 ? 4 * value * value * value : 1 - Math.pow(-2 * value + 2, 3) / 2;
}

function interpolateViewState(progress: number): ViewState {
  const eased = easeInOutCubic(Math.max(0, Math.min(1, progress)));
  return {
    longitude:
      SHOWCASE_MAP_START_VIEW.longitude +
      (SHOWCASE_MAP_END_VIEW.longitude - SHOWCASE_MAP_START_VIEW.longitude) * eased,
    latitude:
      SHOWCASE_MAP_START_VIEW.latitude +
      (SHOWCASE_MAP_END_VIEW.latitude - SHOWCASE_MAP_START_VIEW.latitude) * eased,
    zoom:
      SHOWCASE_MAP_START_VIEW.zoom +
      (SHOWCASE_MAP_END_VIEW.zoom - SHOWCASE_MAP_START_VIEW.zoom) * eased,
    pitch: 0,
    bearing: 0,
  };
}

function demoSliderStep(feature: FeatureMeta): number {
  if (feature.name === 'Estimated price') return 1000;
  if (feature.name === 'Noise (dB)') return 0.05;
  if (feature.name === 'Good+ primary school catchments') return 0.01;
  if (feature.name === 'Travel time to nearest train or tube station (min)') return 0.1;
  return feature.step ?? 1;
}

const FILTER_ROW_STYLES = [
  {
    rail: 'bg-teal-500',
    activeSurface: 'bg-teal-50/40',
    chip: 'bg-coral-50 text-coral-700',
  },
  {
    rail: 'bg-coral-500',
    activeSurface: 'bg-coral-50/40',
    chip: 'bg-teal-50 text-teal-700',
  },
  {
    rail: 'bg-navy-500',
    activeSurface: 'bg-navy-50/50',
    chip: 'bg-navy-50 text-navy-700',
  },
  {
    rail: 'bg-amber-500',
    activeSurface: 'bg-amber-50/50',
    chip: 'bg-amber-50 text-amber-800',
  },
];

function FilterPreviewRow({
  feature,
  value,
  rangeLabel,
  filteredOutCount,
  index,
  isTightened,
  onValueChange,
}: {
  feature: FeatureMeta;
  value: [number, number];
  rangeLabel: string;
  filteredOutCount: number;
  index: number;
  isTightened: boolean;
  onValueChange: (value: [number, number]) => void;
}) {
  const { t } = useTranslation();
  const style = FILTER_ROW_STYLES[index % FILTER_ROW_STYLES.length];
  const shortLabelKeys = {
    'Estimated price': 'home.showcaseFeaturePriceShort',
    'Noise (dB)': 'home.showcaseFeatureNoiseShort',
    'Good+ primary school catchments': 'home.showcaseFeatureSchoolsShort',
    'Travel time to nearest train or tube station (min)': 'home.showcaseFeatureTravelShort',
  } as const;
  const shortLabelKey = shortLabelKeys[feature.name as keyof typeof shortLabelKeys];
  const shortLabel = shortLabelKey ? t(shortLabelKey) : undefined;

  return (
    <div
      className={`relative overflow-hidden rounded-md p-2.5 transition-colors duration-500 dark:bg-navy-900/60 sm:p-3 ${
        isTightened ? style.activeSurface : 'bg-transparent'
      }`}
    >
      <div className={`absolute inset-y-0 left-0 w-1 ${style.rail}`} />
      <div className="flex flex-col gap-2 pl-2 sm:flex-row sm:items-start sm:justify-between sm:gap-4">
        <div className="min-w-0">
          <FeatureLabel feature={feature} size="sm" className="hidden sm:flex" />
          <div className="flex items-center gap-2 text-sm font-semibold text-warm-700 dark:text-warm-300 sm:hidden">
            <FeatureLabel feature={feature} size="sm" label={shortLabel} />
          </div>
          <div className="mt-1 text-sm font-medium text-warm-500 dark:text-warm-400">
            {rangeLabel}
          </div>
        </div>
        <span
          className={`w-fit shrink-0 rounded-md px-2.5 py-1 text-xs font-bold leading-none ${style.chip}`}
        >
          {t('filters.filtersOut', {
            value: filteredOutCount.toLocaleString(),
          })}
        </span>
      </div>
      <div className="mt-3 px-2 pl-4 sm:mt-5">
        <Slider
          min={feature.min ?? 0}
          max={feature.max ?? 100}
          step={demoSliderStep(feature)}
          value={value}
          onValueChange={(nextValue) => onValueChange([nextValue[0], nextValue[1]])}
        />
      </div>
    </div>
  );
}

function formatCompactCurrency(value: number): string {
  if (value >= 1000000) return `£${(value / 1000000).toFixed(1)}m`;
  return `£${Math.round(value / 1000)}k`;
}

function formatDemoRange(feature: FeatureMeta, value: [number, number], t: TFunction): string {
  if (feature.name === 'Estimated price') {
    return `${formatCompactCurrency(value[0])} - ${formatCompactCurrency(value[1])}`;
  }
  if (feature.name === 'Noise (dB)') {
    return `${Math.round(value[0])} - ${Math.round(value[1])} dB`;
  }
  if (feature.name === 'Good+ primary school catchments') {
    return t('home.showcaseGoodPrimariesNearby', { count: Math.round(value[0]) });
  }
  if (feature.name === 'Travel time to nearest train or tube station (min)') {
    return t('home.showcaseWithinRail', { count: Math.round(value[1]) });
  }
  return `${value[0]} - ${value[1]}`;
}

function randomFilteredOutCount(
  feature: FeatureMeta,
  value: [number, number],
  index: number
): number {
  const min = feature.min ?? 0;
  const max = feature.max ?? 100;
  const range = Math.max(max - min, 1);
  const selectedWidth = Math.max(value[1] - value[0], 0);
  const restrictiveness = Math.max(0.08, Math.min(0.92, 1 - selectedWidth / range));
  const bases = [52000, 23000, 17000, 15000];
  const jitter = 0.86 + Math.random() * 0.28;
  const raw = bases[index] * (0.28 + restrictiveness * 0.9) * jitter;
  return Math.max(900, Math.round(raw / 113) * 113);
}

function interpolateRangePath(ranges: [number, number][], progress: number): [number, number] {
  if (progress >= 1) return ranges[ranges.length - 1];

  const segmentCount = Math.max(ranges.length - 1, 1);
  const scaled = Math.max(0, progress) * segmentCount;
  const index = Math.min(Math.floor(scaled), segmentCount - 1);
  const nextIndex = index + 1;
  const localProgress = scaled - index;
  const eased = easeInOutCubic(localProgress);
  const [fromMin, fromMax] = ranges[index];
  const [toMin, toMax] = ranges[nextIndex];

  return [fromMin + (toMin - fromMin) * eased, fromMax + (toMax - fromMax) * eased];
}

function FilterOnlyScreen({ isActive }: { isActive: boolean }) {
  const { t } = useTranslation();
  const [hasUserAdjusted, setHasUserAdjusted] = useState(false);

  const rows = useMemo(
    () => [
      {
        feature: DEMO_FEATURES[0],
        values: [
          [0, 650000],
          [95000, 725000],
          [240000, 535000],
          [135000, 485000],
          [285000, 610000],
          [0, 650000],
        ] as [number, number][],
        filteredOut: [41820, 50622, 24860, 18645, 29796, 41820],
      },
      {
        feature: DEMO_FEATURES[3],
        values: [
          [40, 58],
          [43, 52],
          [40, 58],
        ] as [number, number][],
        filteredOut: [19412, 8706, 19412],
      },
      {
        feature: DEMO_FEATURES[4],
        values: [
          [0, 60],
          [5, 25],
          [0, 60],
        ] as [number, number][],
        filteredOut: [11209, 4118, 11209],
      },
      {
        feature: DEMO_FEATURES[2],
        values: [
          [1, 8],
          [2, 6],
          [1, 8],
        ] as [number, number][],
        filteredOut: [13608, 6944, 13608],
      },
    ],
    []
  );

  const [filterState, setFilterState] = useState(() =>
    rows.map((row) => ({ value: row.values[0], filteredOut: row.filteredOut[0] }))
  );

  useEffect(() => {
    if (!isActive || hasUserAdjusted) return;
    let frame = 0;
    const start = window.performance.now();
    setFilterState(rows.map((row) => ({ value: row.values[0], filteredOut: row.filteredOut[0] })));

    const animate = (timestamp: number) => {
      const progress = ((timestamp - start) % FILTER_ANIMATION_MS) / FILTER_ANIMATION_MS;
      setFilterState(
        rows.map((row) => {
          const value = interpolateRangePath(row.values, progress);
          const filteredOut = interpolateRangePath(
            row.filteredOut.map((count) => [count, count]),
            progress
          )[0];
          return {
            value,
            filteredOut: Math.round(filteredOut),
          };
        })
      );
      frame = window.requestAnimationFrame(animate);
    };

    frame = window.requestAnimationFrame(animate);
    return () => window.cancelAnimationFrame(frame);
  }, [hasUserAdjusted, isActive, rows]);

  const updateFilter = (index: number, value: [number, number]) => {
    setHasUserAdjusted(true);
    setFilterState((current) =>
      current.map((item, itemIndex) =>
        itemIndex === index
          ? {
              value,
              filteredOut: randomFilteredOutCount(rows[index].feature, value, index),
            }
          : item
      )
    );
  };

  return (
    <div className="h-full overflow-y-auto bg-white scrollbar-hide dark:bg-navy-950/50 dark:backdrop-blur-sm">
      <div className="mx-auto grid min-h-full max-w-3xl content-start gap-2 p-2.5 sm:content-center sm:gap-3 sm:p-4">
        {rows.map((row, index) => (
          <div key={row.feature.name} className={index >= 3 ? 'hidden sm:block' : undefined}>
            <FilterPreviewRow
              feature={row.feature}
              value={filterState[index].value}
              rangeLabel={formatDemoRange(row.feature, filterState[index].value, t)}
              filteredOutCount={filterState[index].filteredOut}
              index={index}
              isTightened
              onValueChange={(value) => updateFilter(index, value)}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

function EnglandHexMapScreen({ isActive }: { isActive: boolean }) {
  const { t } = useTranslation();
  const [viewState, setViewState] = useState<ViewState>(SHOWCASE_MAP_START_VIEW);
  const [shouldRenderMap, setShouldRenderMap] = useState(isActive);
  const elapsedRef = useRef(0);
  const lastFrameRef = useRef<number | null>(null);

  useEffect(() => {
    elapsedRef.current = 0;
    lastFrameRef.current = null;
    setViewState(SHOWCASE_MAP_START_VIEW);
    if (isActive) setShouldRenderMap(true);
  }, [isActive]);

  useEffect(() => {
    if (!isActive) return;

    let frame = 0;
    const animate = (timestamp: number) => {
      const lastFrame = lastFrameRef.current ?? timestamp;
      elapsedRef.current = Math.min(
        SHOWCASE_INTERVAL_MS,
        elapsedRef.current + timestamp - lastFrame
      );
      setViewState(interpolateViewState(elapsedRef.current / SHOWCASE_INTERVAL_MS));
      lastFrameRef.current = timestamp;

      if (elapsedRef.current < SHOWCASE_INTERVAL_MS) {
        frame = window.requestAnimationFrame(animate);
      }
    };

    frame = window.requestAnimationFrame(animate);
    return () => window.cancelAnimationFrame(frame);
  }, [isActive]);

  return (
    <div className="pointer-events-none relative h-full overflow-hidden bg-warm-100 dark:bg-navy-950/50">
      {shouldRenderMap && (
        <Suspense fallback={<div className="h-full bg-navy-950/40" aria-hidden="true" />}>
          <ProductMap
            data={SHOWCASE_MAP_DATA}
            postcodeData={EMPTY_SHOWCASE_POSTCODES}
            usePostcodeView={false}
            pois={EMPTY_SHOWCASE_POIS}
            onViewChange={noopViewChange}
            viewFeature={null}
            colorRange={null}
            filterRange={null}
            viewSource={null}
            onCancelPin={() => {}}
            features={DEMO_FEATURES}
            selectedHexagonId={null}
            hoveredHexagonId={null}
            onHexagonClick={noopHexagonClick}
            onHexagonHover={noopHexagonHover}
            initialViewState={viewState}
            theme="dark"
            screenshotMode
            hideLegend
            densityLabel={t('home.showcaseMatchingHomesLabel')}
            totalCount={SHOWCASE_MAP_TOTAL_COUNT}
          />
        </Suspense>
      )}
      <div className="pointer-events-none absolute bottom-4 left-4 max-w-[16rem] rounded-md border border-white/15 bg-navy-950/95 px-4 py-3 text-white shadow-2xl shadow-navy-950/35">
        <div className="text-sm font-black leading-none">Birmingham</div>
        <div className="mt-1 text-xs font-bold leading-tight text-warm-200">
          {t('home.showcaseMatchingHomes', {
            value: SHOWCASE_MAP_TOTAL_COUNT.toLocaleString(),
          })}
        </div>
        <div className="mt-2 text-[10px] font-bold uppercase tracking-wide text-warm-400">
          {t('home.showcaseStep2Sources')}
        </div>
      </div>
    </div>
  );
}

function RightPaneOnlyScreen({
  isActive,
  userScrolledRef,
}: {
  isActive: boolean;
  userScrolledRef: MutableRefObject<boolean>;
}) {
  const { t } = useTranslation();
  const scrollerRef = useRef<HTMLDivElement | null>(null);
  const wasActiveRef = useRef(false);

  useEffect(() => {
    if (isActive && !wasActiveRef.current) {
      userScrolledRef.current = false;
    }
    wasActiveRef.current = isActive;
  }, [isActive, userScrolledRef]);

  useEffect(() => {
    if (!isActive || userScrolledRef.current) return;

    const scroller = scrollerRef.current;
    if (!scroller) return;

    scroller.scrollTop = 0;
    let frame = 0;
    let elapsed = 0;
    let lastFrame: number | null = null;

    const animate = (timestamp: number) => {
      if (userScrolledRef.current) return;

      const previous = lastFrame ?? timestamp;
      elapsed = Math.min(INSPECT_SCROLL_ANIMATION_MS, elapsed + timestamp - previous);
      const maxScroll = Math.max(0, scroller.scrollHeight - scroller.clientHeight);
      scroller.scrollTop = maxScroll * easeInOutCubic(elapsed / INSPECT_SCROLL_ANIMATION_MS);

      lastFrame = timestamp;
      if (elapsed < INSPECT_SCROLL_ANIMATION_MS) {
        frame = window.requestAnimationFrame(animate);
      }
    };

    frame = window.requestAnimationFrame(animate);
    return () => window.cancelAnimationFrame(frame);
  }, [isActive, userScrolledRef]);

  const markUserScrolled = () => {
    if (!isActive) return;
    userScrolledRef.current = true;
  };

  return (
    <div className="h-full overflow-hidden bg-white dark:bg-navy-900/60 dark:backdrop-blur-sm">
      <div className="flex h-full flex-col overflow-hidden">
        <div
          ref={scrollerRef}
          className="min-h-0 flex-1 space-y-2 overflow-y-auto p-2.5 scrollbar-hide sm:space-y-3 sm:p-4"
          onPointerDown={markUserScrolled}
          onTouchStart={markUserScrolled}
          onWheel={markUserScrolled}
        >
          <div className="rounded-lg bg-warm-50 p-3 dark:bg-navy-950/50 sm:p-4">
            <div className="mb-1 flex items-center justify-between gap-3">
              <FeatureLabel feature={DEMO_FEATURES[0]} size="sm" />
              <span className="shrink-0 text-xs font-black text-teal-700 dark:text-teal-300">
                {t('home.showcaseMedianPrice', { value: '£492k' })}
              </span>
            </div>
            <PriceHistoryChart points={PRICE_POINTS} />
          </div>
          <div className="rounded-lg bg-warm-50 p-3 dark:bg-navy-950/50 sm:p-4">
            <div className="mb-2 flex items-center gap-2 text-sm font-semibold text-warm-700 dark:text-warm-300">
              <RouteIcon className="h-4 w-4 text-teal-600 dark:text-teal-400" />
              <span>{t('home.showcaseJourneyRoutes')}</span>
            </div>
            <JourneyInstructions
              postcode="SW5 9AA"
              entries={INSPECT_TRAVEL_ENTRIES}
              label="SW5 9AA"
              presetJourneys={INSPECT_JOURNEYS}
              className="space-y-2"
              showGoogleMapsLink={false}
            />
          </div>
          <div className="rounded-lg bg-warm-50 p-3 dark:bg-navy-950/50 sm:p-4">
            <div className="mb-2 flex items-center justify-between gap-3">
              <FeatureLabel feature={DEMO_FEATURES[2]} size="sm" />
              <span className="shrink-0 text-xs font-black text-teal-700 dark:text-teal-300">
                {t('home.showcaseNearby', {
                  value: formatValue(SCHOOL_NEARBY_COUNT, DEMO_FEATURES[2]),
                })}
              </span>
            </div>
            <DualHistogram
              localCounts={SCHOOL_HISTOGRAM_LOCAL}
              globalCounts={SCHOOL_HISTOGRAM_GLOBAL}
              p1={0}
              p99={8}
              globalMean={SCHOOL_GLOBAL_MEAN}
              formatLabel={(value) => value.toFixed(0)}
            />
          </div>
          <div className="rounded-lg bg-warm-50 p-3 dark:bg-navy-950/50 sm:p-4">
            <div className="mb-2 flex items-center gap-2 text-sm font-semibold text-warm-700 dark:text-warm-300">
              <ChartBarIcon className="h-4 w-4 text-teal-600 dark:text-teal-400" />
              <span>{t('home.showcaseStep3Stat2Label')}</span>
            </div>
            <StackedBarChart
              segments={CRIME_SEGMENTS}
              total={82}
              colorMap={STACKED_SEGMENT_COLORS}
            />
          </div>
          <div className="rounded-lg bg-warm-50 p-3 dark:bg-navy-950/50 sm:p-4">
            <div className="mb-2 flex items-center justify-between gap-3">
              <div className="flex min-w-0 items-center gap-2 text-sm font-semibold text-warm-700 dark:text-warm-300">
                <ChartBarIcon className="h-4 w-4 shrink-0 text-teal-600 dark:text-teal-400" />
                <span className="truncate">{t('home.showcasePoliticalVoteShare')}</span>
              </div>
              <span className="shrink-0 text-xs font-black text-teal-700 dark:text-teal-300">
                2024 GE
              </span>
            </div>
            <StackedBarChart
              segments={VOTE_SHARE_SEGMENTS}
              total={100}
              colorMap={PARTY_FEATURE_COLORS}
            />
          </div>
          <div className="rounded-lg border border-dashed border-warm-200 bg-white/80 px-4 py-3 text-center text-sm font-black text-warm-500 dark:border-navy-700 dark:bg-navy-950/70 dark:text-warm-400">
            {t('home.showcaseLotsMore')}
          </div>
        </div>
      </div>
    </div>
  );
}

function ScoutScreen({ isActive }: { isActive: boolean }) {
  const { t } = useTranslation();
  const [isTableRevealed, setIsTableRevealed] = useState(false);
  const scoutRows = [
    {
      postcode: 'SW5 9AA',
      score: '94%',
      commute: t('home.showcaseMinutes', { count: 23 }),
      price: '£492k',
    },
    {
      postcode: 'SE22 8EF',
      score: '91%',
      commute: t('home.showcaseMinutes', { count: 28 }),
      price: '£518k',
    },
    {
      postcode: 'N4 2AB',
      score: '88%',
      commute: t('home.showcaseMinutes', { count: 31 }),
      price: '£476k',
    },
  ];

  useEffect(() => {
    if (!isActive) {
      setIsTableRevealed(false);
      return;
    }

    const prefersReducedMotion =
      typeof window.matchMedia === 'function' &&
      window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (prefersReducedMotion) {
      setIsTableRevealed(true);
      return;
    }

    setIsTableRevealed(false);
    const timer = window.setTimeout(() => setIsTableRevealed(true), SCOUT_TABLE_REVEAL_MS);
    return () => window.clearTimeout(timer);
  }, [isActive]);

  return (
    <div
      className={`relative flex h-full min-h-0 flex-col overflow-hidden bg-[#f7f3ed] p-3 dark:bg-navy-950/45 sm:p-5 ${
        isActive ? 'scout-screen-active' : ''
      }`}
    >
      <div className="my-auto w-full shrink-0">
        <div className="relative z-10 shrink-0 rounded-lg bg-white p-2 shadow-2xl shadow-navy-950/10 dark:bg-navy-900/65 dark:backdrop-blur-sm sm:p-5">
          <div className="grid grid-cols-2 gap-2 sm:gap-4">
            <div className="cursor-default select-none rounded-lg border border-warm-200 bg-warm-50 p-2 shadow-sm dark:border-navy-700 dark:bg-navy-950/50 sm:p-4">
              <div className="flex items-center gap-3">
                <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-teal-50 text-teal-700 dark:bg-teal-900/30 dark:text-teal-300 sm:h-10 sm:w-10">
                  <ClipboardIcon className="h-4 w-4 sm:h-5 sm:w-5" />
                </span>
                <div className="min-w-0">
                  <div className="text-sm font-black text-navy-950 dark:text-warm-100 sm:text-base">
                    {t('common.share')}
                  </div>
                  <div className="mt-0.5 hidden truncate text-xs font-semibold text-warm-500 dark:text-warm-400 sm:block">
                    {t('home.showcaseSendShortlist')}
                  </div>
                </div>
              </div>
            </div>
            <div className="scout-export-action relative cursor-default select-none overflow-hidden rounded-lg border border-teal-300 bg-teal-600 p-2 text-white shadow-lg shadow-teal-900/20 dark:border-teal-500 dark:bg-teal-500 dark:text-navy-950 sm:p-4">
              <span className="scout-export-ripple" aria-hidden="true" />
              <div className="relative flex items-center gap-2 sm:gap-3">
                <span className="scout-export-icon flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-white/15 text-white dark:bg-navy-950/10 dark:text-navy-950 sm:h-10 sm:w-10">
                  <DownloadIcon className="h-4 w-4 sm:h-5 sm:w-5" />
                </span>
                <div className="min-w-0">
                  <div className="text-sm font-black sm:text-base">{t('header.exportLabel')}</div>
                  <div className="mt-0.5 hidden truncate text-xs font-semibold text-teal-50 dark:text-navy-800 sm:block">
                    {t('home.showcaseDownloadXlsx')}
                  </div>
                </div>
                <span className="scout-export-check absolute left-4 top-0 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-white text-teal-700 shadow-sm dark:bg-navy-950 dark:text-teal-300 sm:static sm:ml-auto sm:h-6 sm:w-6">
                  <CheckIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
                </span>
              </div>
            </div>
          </div>
        </div>

        <div
          className={`overflow-hidden transition-all duration-500 ease-out ${
            isTableRevealed ? 'mt-3 max-h-64 opacity-100 sm:mt-4' : 'mt-0 max-h-0 opacity-0'
          }`}
          aria-hidden={!isTableRevealed}
        >
          <div className="relative z-10 shrink-0 overflow-hidden rounded-lg border border-warm-200 bg-white shadow-xl shadow-navy-950/10 ring-1 ring-white/80 dark:border-navy-700 dark:bg-navy-900/70 dark:ring-white/5 dark:backdrop-blur-sm">
            <div className="flex items-center justify-between gap-3 border-b border-warm-200 bg-gradient-to-r from-white via-emerald-50/80 to-white px-3 py-1.5 text-navy-950 dark:border-navy-700 dark:from-navy-900/90 dark:via-emerald-900/20 dark:to-navy-900/90 dark:text-warm-100 sm:px-4 sm:py-2">
              <div className="flex min-w-0 items-center gap-2">
                <span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-emerald-600 text-white shadow-sm shadow-emerald-900/20 dark:bg-emerald-400 dark:text-navy-950 sm:h-7 sm:w-7">
                  <DownloadIcon className="h-3.5 w-3.5" />
                </span>
                <span className="min-w-0 truncate text-xs font-black sm:text-sm">
                  {t('home.showcaseStep4FileName')}
                </span>
              </div>
              <span className="shrink-0 rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-[10px] font-black text-emerald-800 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-200 sm:text-xs">
                {t('home.showcaseTopThree')}
              </span>
            </div>
            <div className="grid grid-cols-[1.25fr_0.85fr_0.9fr_1fr] border-b border-warm-200 bg-warm-50 text-[10px] font-black uppercase text-warm-500 dark:border-navy-700 dark:bg-navy-950/45 dark:text-warm-400 sm:text-xs">
              {[
                t('home.showcaseStep4ColPostcode'),
                t('home.showcaseStep4ColScore'),
                t('home.showcaseStep4ColCommute'),
                t('home.showcaseStep4ColPrice'),
              ].map((heading) => (
                <div
                  key={heading}
                  className="truncate border-r border-warm-200 px-2 py-1.5 last:border-r-0 dark:border-navy-700 sm:px-3 sm:py-2"
                >
                  {heading}
                </div>
              ))}
            </div>
            {scoutRows.map((row, index) => (
              <div
                key={row.postcode}
                className={`grid grid-cols-[1.25fr_0.85fr_0.9fr_1fr] text-[11px] font-semibold text-navy-950 dark:text-warm-100 sm:text-sm ${
                  index % 2 === 0
                    ? 'bg-white dark:bg-navy-900/55'
                    : 'bg-warm-50/70 dark:bg-navy-950/35'
                }`}
              >
                <div className="flex min-w-0 items-center gap-1.5 border-r border-warm-200 px-2 py-1.5 dark:border-navy-700 sm:px-3 sm:py-2.5">
                  <span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md bg-emerald-50 text-[10px] font-black text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300">
                    {index + 1}
                  </span>
                  <span className="truncate font-black">{row.postcode}</span>
                </div>
                <div className="min-w-0 border-r border-warm-200 px-2 py-1.5 dark:border-navy-700 sm:px-3 sm:py-2.5">
                  <div className="flex items-center gap-2">
                    <span className="shrink-0 font-black text-emerald-700 dark:text-emerald-300">
                      {row.score}
                    </span>
                    <span className="hidden h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-warm-200 dark:bg-navy-700 sm:block">
                      <span
                        className="block h-full rounded-full bg-emerald-500 dark:bg-emerald-300"
                        style={{ width: row.score }}
                      />
                    </span>
                  </div>
                </div>
                <div className="truncate border-r border-warm-200 px-2 py-1.5 dark:border-navy-700 sm:px-3 sm:py-2.5">
                  {row.commute}
                </div>
                <div className="truncate px-2 py-1.5 font-black sm:px-3 sm:py-2.5">{row.price}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="relative z-10 mt-3 shrink-0 rounded-lg bg-navy-950/55 p-3 text-left text-white shadow-2xl shadow-navy-950/20 backdrop-blur-sm sm:mt-4 sm:p-4">
          <div className="text-base font-black leading-tight">
            {t('home.showcaseStep4Conclusion')}
          </div>
          <div className="mt-3 grid gap-1.5 text-xs font-medium leading-relaxed text-warm-300 sm:gap-2">
            {[
              t('home.showcaseScoutBullet1'),
              t('home.showcaseScoutBullet2'),
              t('home.showcaseScoutBullet3'),
            ].map((item) => (
              <div key={item} className="flex gap-2">
                <CheckIcon className="mt-0.5 h-4 w-4 shrink-0 text-teal-300" />
                <span>{item}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function DashboardShowcase({
  activeStep,
  active,
  hasStarted,
  inspectUserScrolledRef,
}: {
  activeStep: number;
  active: ShowcaseStep;
  hasStarted: boolean;
  inspectUserScrolledRef: MutableRefObject<boolean>;
}) {
  const screens = [
    <FilterOnlyScreen key="filter" isActive={hasStarted && activeStep === 0} />,
    <EnglandHexMapScreen key="match" isActive={hasStarted && activeStep === 1} />,
    <RightPaneOnlyScreen
      key="inspect"
      isActive={hasStarted && activeStep === 2}
      userScrolledRef={inspectUserScrolledRef}
    />,
    <ScoutScreen key="scout" isActive={hasStarted && activeStep === 3} />,
  ];
  const ActiveIcon = active.Icon;
  const showStageHeader = activeStep !== 3;

  return (
    <div className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-warm-100 text-navy-950 dark:bg-navy-950/45 dark:text-warm-100">
      {showStageHeader && (
        <div className="shrink-0 bg-navy-950/55 px-3 py-2 text-white shadow-sm backdrop-blur-sm sm:px-4 sm:py-3">
          <div className="flex items-start gap-2 sm:gap-3">
            <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-teal-400/10 text-teal-200 sm:h-9 sm:w-9">
              <ActiveIcon className="h-4 w-4" />
            </div>
            <div className="min-w-0">
              <div className="text-xs font-bold uppercase text-teal-200">{active.tab}</div>
              <h2 className="mt-0.5 text-base font-black leading-tight text-white sm:mt-1 sm:text-lg">
                {active.title}
              </h2>
              <p className="mt-1 hidden text-sm leading-relaxed text-warm-300 sm:block">
                {active.body}
              </p>
            </div>
          </div>
        </div>
      )}
      <div className="relative min-h-0 min-w-0 flex-1 overflow-hidden">
        {screens.map((screen, index) => (
          <div
            key={index}
            className={`absolute inset-0 transition-opacity duration-300 ${
              activeStep === index
                ? 'visible z-10 opacity-100'
                : 'invisible z-0 opacity-0 pointer-events-none'
            }`}
            aria-hidden={activeStep !== index}
          >
            {screen}
          </div>
        ))}
      </div>
    </div>
  );
}

export default function ProductShowcase({ className = '' }: ProductShowcaseProps) {
  const { t } = useTranslation();
  const [activeStep, setActiveStep] = useState(0);
  const [isStagePaused, setIsStagePaused] = useState(false);
  const [hasStarted, setHasStarted] = useState(false);
  const [canPauseOnHover, setCanPauseOnHover] = useState(false);
  const showcaseRef = useRef<HTMLDivElement | null>(null);
  const inspectUserScrolledRef = useRef(false);

  const steps: ShowcaseStep[] = [
    {
      tab: t('home.showcaseStep1Tab'),
      title: t('home.showcaseStep1Title'),
      body: t('home.showcaseStep1Body'),
      Icon: FilterIcon,
    },
    {
      tab: t('home.showcaseStep2Tab'),
      title: t('home.showcaseStep2Title'),
      body: t('home.showcaseStep2Body'),
      Icon: MapPinIcon,
    },
    {
      tab: t('home.showcaseStep3Tab'),
      title: t('home.showcaseStep3Title'),
      body: t('home.showcaseStep3Body'),
      Icon: ChartBarIcon,
    },
    {
      tab: t('home.showcaseStep4Tab'),
      title: t('home.showcaseStep4Title'),
      body: t('home.showcaseStep4Body'),
      Icon: RouteIcon,
    },
  ];

  const active = steps[activeStep];
  const activeStepIntervalMs = SHOWCASE_STEP_INTERVALS_MS[activeStep] ?? SHOWCASE_INTERVAL_MS;
  const isProgressRunning = hasStarted && !isStagePaused;

  useEffect(() => {
    const showcase = showcaseRef.current;
    if (!showcase || hasStarted) return;

    if (!('IntersectionObserver' in window)) {
      setHasStarted(true);
      return;
    }

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (!entry.isIntersecting) return;
        setHasStarted(true);
        observer.disconnect();
      },
      { threshold: 0.2 }
    );

    observer.observe(showcase);
    return () => observer.disconnect();
  }, [hasStarted]);

  useEffect(() => {
    if (typeof window.matchMedia !== 'function') return;

    const mediaQuery = window.matchMedia('(hover: hover) and (pointer: fine)');
    const updateCanPause = () => {
      setCanPauseOnHover(mediaQuery.matches);
      if (!mediaQuery.matches) setIsStagePaused(false);
    };

    updateCanPause();
    mediaQuery.addEventListener('change', updateCanPause);
    return () => mediaQuery.removeEventListener('change', updateCanPause);
  }, []);

  const pauseForHover = () => {
    if (canPauseOnHover) setIsStagePaused(true);
  };

  const resumeAfterHover = () => {
    if (canPauseOnHover) setIsStagePaused(false);
  };

  return (
    <div
      ref={showcaseRef}
      className={`product-showcase dark relative w-full min-w-0 max-w-[58rem] justify-self-center ${className}`.trim()}
      onMouseEnter={pauseForHover}
      onMouseLeave={resumeAfterHover}
      onFocus={pauseForHover}
      onBlur={resumeAfterHover}
      aria-label={t('home.showcaseContext')}
    >
      <div className="product-showcase-frame flex h-[36rem] flex-col overflow-hidden rounded-lg bg-navy-950/50 shadow-2xl shadow-black/40 backdrop-blur-sm ring-1 ring-white/10 sm:h-[38rem] md:h-[40rem]">
        <div className="shrink-0 bg-white/[0.035] p-1.5 sm:p-2 md:p-3">
          <div className="grid grid-cols-4 gap-1 sm:gap-2">
            {steps.map((step, index) => {
              const Icon = step.Icon;
              return (
                <button
                  key={step.tab}
                  type="button"
                  onClick={() => setActiveStep(index)}
                  aria-pressed={activeStep === index}
                  className={`rounded-md px-1.5 py-2 text-left transition-colors sm:px-3 ${
                    activeStep === index
                      ? 'bg-teal-400/10 text-white shadow-sm'
                      : 'bg-white/[0.03] text-warm-200 hover:bg-white/[0.06] hover:text-white'
                  }`}
                >
                  <div className="flex items-center justify-center gap-1 sm:justify-start sm:gap-2">
                    <Icon
                      className={`hidden h-4 w-4 sm:block ${
                        activeStep === index ? 'text-teal-300' : 'text-warm-300'
                      }`}
                    />
                    <span className="text-[11px] font-bold sm:hidden">{index + 1}</span>
                    <span className="hidden text-xs font-bold sm:inline">
                      {index + 1}. {step.tab}
                    </span>
                  </div>
                  <span className="mt-1.5 block h-1 overflow-hidden rounded-full bg-white/10 sm:mt-2">
                    {hasStarted && activeStep === index && (
                      <span
                        key={activeStep}
                        className="showcase-progress block h-full origin-left bg-teal-400"
                        style={{
                          animationDuration: `${activeStepIntervalMs}ms`,
                          animationPlayState: isProgressRunning ? 'running' : 'paused',
                        }}
                        onAnimationEnd={() =>
                          setActiveStep((step) => (step + 1) % SHOWCASE_STEP_COUNT)
                        }
                      />
                    )}
                  </span>
                </button>
              );
            })}
          </div>
        </div>
        <div className="min-h-0 min-w-0 flex-1">
          <DashboardShowcase
            activeStep={activeStep}
            active={active}
            hasStarted={hasStarted}
            inspectUserScrolledRef={inspectUserScrolledRef}
          />
        </div>
      </div>
    </div>
  );
}
