import { useMemo, useState, type MutableRefObject, type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { ts } from '../../i18n/server';
import type {
  FeatureFilters,
  FeatureGroup,
  FeatureMeta,
  FilterExclusion,
  HexagonStatsResponse,
} from '../../types';
import { travelFieldKey, type TravelTimeEntry } from '../../hooks/useTravelTime';
import type { HexagonLocation } from '../../lib/external-search';
import { formatStationDistance, type NearbyStation } from '../../lib/nearby-stations';
import {
  formatValue,
  formatFilterValue,
  calculateHistogramMean,
  roundedPercentages,
} from '../../lib/format';
import { groupFeaturesByCategory } from '../../lib/features';
import { getPoiCategoryLogoUrl } from '../../lib/map-utils';
import {
  getActiveAmenityFilterFeatureNames,
  isPoiFilterFeatureName,
} from '../../lib/poi-distance-filter';
import {
  PARTY_FEATURE_COLORS,
  STACKED_GROUPS,
  STACKED_ENUM_GROUPS,
  STACKED_SEGMENT_COLORS,
} from '../../lib/consts';
import { useNearbyStations } from '../../hooks/useNearbyStations';
import { useRetainedScrollTop } from '../../hooks/useRetainedScrollTop';
import { DualHistogram, LoadingSkeleton } from './DualHistogram';
import EnumBarChart from './EnumBarChart';
import StackedBarChart from './StackedBarChart';
import StackedEnumChart from './StackedEnumChart';
import PriceHistoryChart from './PriceHistoryChart';
import CrimeYearChart from './CrimeYearChart';
import ExternalSearchLinks from './ExternalSearchLinks';
import { InfoIcon, TransitIcon } from '../ui/icons';
import { CollapsibleGroupHeader } from '../ui/CollapsibleGroupHeader';
import { FeatureInfoPopup } from '../ui/FeatureInfoPopup';
import { EmptyState } from '../ui/EmptyState';
import { FeatureLabel } from '../ui/FeatureLabel';
import { IndeterminateProgressBar } from '../ui/IndeterminateProgressBar';
import StreetViewEmbed from './StreetViewEmbed';
import JourneyInstructions from './JourneyInstructions';

interface AreaPaneProps {
  stats: HexagonStatsResponse | null;
  globalFeatures: FeatureMeta[];
  loading: boolean;
  hexagonId: string | null;
  isPostcode?: boolean;
  hexagonLocation: HexagonLocation | null;
  filters: FeatureFilters;
  unfilteredCount?: number | null;
  statsUseFilters: boolean;
  onStatsUseFiltersChange: (useFilters: boolean) => void;
  onNavigateToSource?: (slug: string, featureName: string) => void;
  travelTimeEntries?: TravelTimeEntry[];
  shareCode?: string;
  isGroupExpanded: (name: string) => boolean;
  onToggleGroup: (name: string) => void;
  scrollTopRef?: MutableRefObject<number>;
  scrollRestoreKey?: string | null;
  scrollSaveDisabled?: boolean;
}

function normalizePercentageSegments<T extends { value: number }>(segments: T[]): T[] {
  const total = segments.reduce((sum, segment) => sum + segment.value, 0);
  const normalizedValues = roundedPercentages(
    segments.map((segment) => segment.value),
    total,
    1
  );
  return segments.map((segment, index) => ({ ...segment, value: normalizedValues[index] }));
}

function filterValueFormat(feature?: FeatureMeta) {
  if (!feature) return undefined;
  return {
    prefix: feature.prefix,
    suffix: feature.suffix,
    raw: feature.raw,
  };
}

const STATION_GROUP_NAME = 'Transport';
const STATION_GROUP_NAMES = new Set([STATION_GROUP_NAME, 'Public Transport']);

function MetricTextLabel({ children }: { children: ReactNode }) {
  return (
    <span className="block min-w-0 flex-1 break-words text-[13px] font-medium leading-snug text-warm-900 dark:text-warm-100">
      {children}
    </span>
  );
}

function MetricFeatureLabel({
  feature,
  onShowInfo,
  label,
  aboutLabel,
}: {
  feature: FeatureMeta;
  onShowInfo: (feature: FeatureMeta) => void;
  label?: string;
  aboutLabel: string;
}) {
  return (
    <div className="flex min-w-0 items-start gap-1.5">
      <MetricTextLabel>{label ?? ts(feature.name)}</MetricTextLabel>
      {feature.detail && (
        <button
          type="button"
          onClick={() => onShowInfo(feature)}
          className="-m-1 shrink-0 rounded p-1 text-warm-400 hover:bg-warm-100 hover:text-warm-700 dark:hover:bg-navy-800 dark:hover:text-warm-200"
          title={aboutLabel}
          aria-label={aboutLabel}
        >
          <InfoIcon className="h-3.5 w-3.5" />
        </button>
      )}
    </div>
  );
}

function MetricRow({
  label,
  chart,
  value,
  valueTitle,
  className = '',
}: {
  label: ReactNode;
  chart?: ReactNode;
  value?: ReactNode;
  valueTitle?: string;
  className?: string;
}) {
  return (
    <div
      className={`grid min-h-10 grid-cols-[minmax(0,1fr)_6.5rem_minmax(3.5rem,auto)] items-center gap-3 py-1.5 ${className}`}
    >
      <div className="min-w-0">{label}</div>
      <div className="w-[6.5rem] justify-self-end">{chart}</div>
      <div
        className="min-w-[3.5rem] max-w-[7rem] truncate text-right text-sm font-semibold leading-tight tabular-nums text-navy-950 dark:text-warm-50"
        title={valueTitle}
      >
        {value}
      </div>
    </div>
  );
}

function NearbyStationsCard({ location }: { location: HexagonLocation }) {
  const { t } = useTranslation();
  const origin = useMemo(
    () => ({ lat: location.lat, lon: location.lon }),
    [location.lat, location.lon]
  );
  const { stations, loading } = useNearbyStations(origin);

  return (
    <div className="py-1.5">
      <div className="flex items-center gap-2 py-1">
        <TransitIcon className="h-4 w-4 text-teal-600 dark:text-teal-400" />
        <MetricTextLabel>{t('areaPane.closestStations')}</MetricTextLabel>
        {loading && (
          <span className="ml-auto h-3 w-3 rounded-full border-2 border-teal-600 border-t-transparent dark:border-teal-400 dark:border-t-transparent animate-spin" />
        )}
      </div>
      {stations.length > 0 ? (
        <ol className="divide-y divide-warm-100 dark:divide-navy-800">
          {stations.map((station) => (
            <NearbyStationRow key={station.id} station={station} />
          ))}
        </ol>
      ) : (
        <div className="py-2 text-sm text-warm-500 dark:text-warm-400">
          {loading ? t('common.loading') : t('areaPane.noNearbyStations')}
        </div>
      )}
    </div>
  );
}

function NearbyStationRow({ station }: { station: NearbyStation }) {
  const icon = getPoiCategoryLogoUrl(station.category, station.icon_category);

  return (
    <li className="flex items-center gap-2 px-3 py-2">
      {icon ? (
        <img
          src={icon}
          alt=""
          aria-hidden="true"
          loading="lazy"
          className="h-5 w-5 shrink-0 rounded-[3px] bg-white object-contain p-0.5"
        />
      ) : (
        <TransitIcon className="h-5 w-5 shrink-0 text-warm-400 dark:text-warm-500" />
      )}
      <div className="min-w-0 flex-1">
        <div className="truncate text-sm font-medium text-warm-900 dark:text-warm-100">
          {station.name}
        </div>
        <div className="text-xs text-warm-500 dark:text-warm-400">{ts(station.category)}</div>
      </div>
      <span className="shrink-0 text-sm font-semibold tabular-nums text-teal-700 dark:text-teal-400">
        {formatStationDistance(station.distanceKm)}
      </span>
    </li>
  );
}

export default function AreaPane({
  stats,
  globalFeatures,
  loading,
  hexagonId,
  isPostcode = false,
  hexagonLocation,
  filters,
  unfilteredCount,
  statsUseFilters,
  onStatsUseFiltersChange,
  onNavigateToSource,
  travelTimeEntries,
  shareCode,
  isGroupExpanded,
  onToggleGroup,
  scrollTopRef,
  scrollRestoreKey,
  scrollSaveDisabled,
}: AreaPaneProps) {
  const { t } = useTranslation();
  const propertyCount = stats?.count;
  const activeFilterCount = Object.keys(filters).length + (travelTimeEntries?.length ?? 0);
  const filtersActive = activeFilterCount > 0;
  const filteredStatsEmpty = filtersActive && statsUseFilters && stats?.count === 0;
  const showFlipToggleCallout = filteredStatsEmpty && unfilteredCount !== 0;
  const activeFilterNames = useMemo(() => new Set(Object.keys(filters)), [filters]);
  const activeAmenityFeatureNames = useMemo(
    () => getActiveAmenityFilterFeatureNames(filters),
    [filters]
  );
  const featureGroups = useMemo(() => groupFeaturesByCategory(globalFeatures), [globalFeatures]);
  const paneFeatureGroups = useMemo<FeatureGroup[]>(
    () =>
      featureGroups
        .map((group) => {
          if (group.name !== 'Amenities') return group;

          const features = group.features.filter((feature) => {
            if (isPoiFilterFeatureName(feature.name)) {
              return activeAmenityFeatureNames.has(feature.name);
            }
            return activeFilterNames.has(feature.name);
          });

          return { ...group, features };
        })
        .filter((group) => group.name !== 'Amenities' || group.features.length > 0),
    [activeAmenityFeatureNames, activeFilterNames, featureGroups]
  );
  const displayFeatureGroups = useMemo<FeatureGroup[]>(() => {
    if (
      !hexagonLocation ||
      paneFeatureGroups.some((group) => STATION_GROUP_NAMES.has(group.name))
    ) {
      return paneFeatureGroups;
    }

    return [{ name: STATION_GROUP_NAME, features: [] }, ...paneFeatureGroups];
  }, [paneFeatureGroups, hexagonLocation]);
  const [infoFeature, setInfoFeature] = useState<FeatureMeta | null>(null);
  const { scrollRef, onScroll } = useRetainedScrollTop<HTMLDivElement>({
    restoreKey: scrollRestoreKey ?? hexagonId,
    scrollTopRef,
    suspendSave: scrollSaveDisabled ?? (loading && stats == null),
  });

  const numericByName = useMemo(() => {
    if (!stats) return new Map();
    return new Map(stats.numeric_features.map((feature) => [feature.name, feature]));
  }, [stats]);

  const enumByName = useMemo(() => {
    if (!stats) return new Map();
    return new Map(stats.enum_features.map((feature) => [feature.name, feature]));
  }, [stats]);

  // Crime-by-year series is keyed in the API by the bare crime type (e.g. "Burglary").
  // We also index by the configured feature name (with " (avg/yr)" suffix) so the
  // metric-row renderer can look it up using the feature name it already has.
  const crimeByYearByFeatureName = useMemo(() => {
    const map = new Map<string, NonNullable<HexagonStatsResponse['crime_by_year']>[number]>();
    for (const entry of stats?.crime_by_year ?? []) {
      map.set(entry.name, entry);
      map.set(`${entry.name} (avg/yr)`, entry);
    }
    return map;
  }, [stats]);

  const globalFeatureByName = useMemo(
    () => new Map(globalFeatures.map((f) => [f.name, f])),
    [globalFeatures]
  );
  const travelEntryByField = useMemo(() => {
    const map = new Map<string, TravelTimeEntry>();
    for (const entry of travelTimeEntries ?? []) {
      map.set(travelFieldKey(entry), entry);
    }
    return map;
  }, [travelTimeEntries]);
  const filterExclusions = stats?.filter_exclusions ?? [];

  const getExclusionLabel = (exclusion: FilterExclusion) => {
    const travelEntry = travelEntryByField.get(exclusion.name);
    if (travelEntry) return t('areaPane.travelTo', { destination: travelEntry.label });
    return ts(exclusion.name);
  };

  const formatExclusionValue = (exclusion: FilterExclusion, value: number) => {
    if (exclusion.kind === 'travel') return `${Math.round(value)} ${t('common.minute')}`;
    return formatFilterValue(value, filterValueFormat(globalFeatureByName.get(exclusion.name)));
  };

  const getExclusionAdjustment = (exclusion: FilterExclusion) => {
    if (exclusion.direction === 'missing_value') {
      return t('areaPane.missingFilterValue');
    }
    if (exclusion.direction === 'allow_value') {
      return t('areaPane.allowCategory', { value: ts(exclusion.category ?? '') });
    }
    if (exclusion.value == null) return '';
    const value = formatExclusionValue(exclusion, exclusion.value);
    return exclusion.direction === 'lower_min'
      ? t('areaPane.lowerMinTo', { value })
      : t('areaPane.raiseMaxTo', { value });
  };

  if (!hexagonId) {
    return (
      <EmptyState
        icon={<InfoIcon className="w-8 h-8 text-warm-300 dark:text-warm-600" />}
        title={t('common.noAreaSelected')}
        description={t('common.noAreaSelectedDesc')}
        centered
      />
    );
  }

  return (
    <>
      <div className="relative flex h-full flex-col">
        <IndeterminateProgressBar show={loading && stats != null} />
        <div ref={scrollRef} onScroll={onScroll} className="flex-1 overflow-y-auto">
          <div className="border-b border-warm-200 bg-white dark:border-navy-700 dark:bg-navy-950">
            <div className="space-y-3 p-3">
              <div className="flex items-start justify-between gap-3">
                <div className="min-w-0">
                  <div className="flex min-w-0 items-center gap-2">
                    <h2 className="truncate text-base font-semibold text-warm-900 dark:text-warm-100">
                      {isPostcode ? hexagonId : t('areaPane.areaOverview')}
                    </h2>
                    {loading && (
                      <span className="h-3 w-3 shrink-0 rounded-full border-2 border-teal-600 border-t-transparent dark:border-teal-400 dark:border-t-transparent animate-spin" />
                    )}
                  </div>
                  <p className="mt-0.5 text-xs leading-snug text-warm-500 dark:text-warm-400">
                    {t('areaPane.statsFor', {
                      type: isPostcode
                        ? t('common.postcode').toLowerCase()
                        : t('common.area').toLowerCase(),
                    })}
                  </p>
                </div>
                <div className="shrink-0 text-right">
                  <div className="text-lg font-semibold tabular-nums leading-none text-navy-950 dark:text-warm-50">
                    {propertyCount == null ? '...' : propertyCount.toLocaleString()}
                  </div>
                  <div className="mt-0.5 text-xs font-medium text-warm-500 dark:text-warm-400">
                    {t('common.propertiesPlural')}
                  </div>
                </div>
              </div>

              <div className="rounded border border-warm-200 bg-warm-50 px-2.5 py-2 dark:border-navy-700 dark:bg-navy-900">
                <div className="flex flex-wrap items-center justify-between gap-2">
                  <span className="text-xs font-semibold text-warm-700 dark:text-warm-200">
                    {t('areaPane.statsBasis')}
                  </span>
                  <div className="grid min-w-0 flex-1 basis-52 grid-cols-2 rounded-md bg-warm-200 p-0.5 dark:bg-navy-800">
                    <button
                      type="button"
                      disabled={!filtersActive}
                      aria-pressed={statsUseFilters && filtersActive}
                      onClick={() => onStatsUseFiltersChange(true)}
                      className={`min-w-0 rounded px-2 py-1 text-center text-xs font-medium leading-tight break-words ${
                        statsUseFilters && filtersActive
                          ? 'bg-white text-teal-700 shadow-sm dark:bg-navy-700 dark:text-teal-300'
                          : 'text-warm-600 hover:text-warm-900 disabled:cursor-not-allowed disabled:opacity-50 dark:text-warm-400 dark:hover:text-warm-100'
                      }`}
                    >
                      {t('areaPane.matchingFiltersOption')}
                    </button>
                    <button
                      type="button"
                      aria-pressed={!statsUseFilters || !filtersActive}
                      onClick={() => onStatsUseFiltersChange(false)}
                      className={`min-w-0 rounded px-2 py-1 text-center text-xs font-medium leading-tight break-words ${
                        !statsUseFilters || !filtersActive
                          ? 'bg-white text-teal-700 shadow-sm dark:bg-navy-700 dark:text-teal-300'
                          : 'text-warm-600 hover:text-warm-900 dark:text-warm-400 dark:hover:text-warm-100'
                      }`}
                    >
                      {t('areaPane.allPropertiesOption')}
                    </button>
                  </div>
                </div>
                <p className="mt-1.5 text-xs leading-snug text-warm-500 dark:text-warm-400">
                  {filtersActive
                    ? statsUseFilters
                      ? t('areaPane.filtersAffectStats', { count: activeFilterCount })
                      : t('areaPane.filtersIgnoredForStats')
                    : t('areaPane.noFiltersAffectStats')}
                </p>
              </div>

              {showFlipToggleCallout && (
                <div className="mt-2 rounded border border-amber-200 bg-amber-50 px-2.5 py-2 text-xs leading-snug text-amber-900 dark:border-amber-800/70 dark:bg-amber-950/40 dark:text-amber-100">
                  <p className="font-semibold">{t('areaPane.filteredStatsEmpty')}</p>
                  <p className="mt-1">
                    {unfilteredCount != null
                      ? t('areaPane.showAllStatsHint', { count: unfilteredCount })
                      : t('areaPane.showAllStatsFallback')}
                  </p>
                  <button
                    type="button"
                    onClick={() => onStatsUseFiltersChange(false)}
                    className="mt-2 rounded bg-amber-600 px-2 py-1 text-xs font-medium text-white hover:bg-amber-700 dark:bg-amber-500 dark:text-amber-950 dark:hover:bg-amber-400"
                  >
                    {t('areaPane.showAllStats')}
                  </button>
                  {filterExclusions.length > 0 && (
                    <div className="mt-2 border-t border-amber-200 pt-2 dark:border-amber-800/70">
                      <p className="font-semibold">{t('areaPane.closestBlockingFilters')}</p>
                      <ol className="mt-1.5 space-y-1.5">
                        {filterExclusions.map((exclusion) => (
                          <li
                            key={`${exclusion.kind}:${exclusion.name}:${exclusion.direction}:${exclusion.category ?? ''}`}
                            className="rounded bg-white/70 px-2 py-1.5 dark:bg-navy-950/40"
                          >
                            <div className="break-words font-medium leading-snug">
                              {getExclusionLabel(exclusion)}
                            </div>
                            <p className="mt-0.5 text-amber-800/80 dark:text-amber-100/80">
                              {getExclusionAdjustment(exclusion)}
                            </p>
                          </li>
                        ))}
                      </ol>
                    </div>
                  )}
                </div>
              )}
            </div>
          </div>

          {hexagonLocation && stats && (
            <ExternalSearchLinks location={hexagonLocation} filters={filters} />
          )}
          {(() => {
            const journeyPostcode = isPostcode ? hexagonId : stats?.central_postcode;
            return journeyPostcode && travelTimeEntries && travelTimeEntries.length > 0 ? (
              <JourneyInstructions
                postcode={journeyPostcode}
                entries={travelTimeEntries}
                label={!isPostcode ? journeyPostcode : undefined}
                shareCode={shareCode}
              />
            ) : null;
          })()}
          {loading && !stats ? (
            <LoadingSkeleton />
          ) : stats ? (
            <div>
              {hexagonLocation && <StreetViewEmbed location={hexagonLocation} />}
              {stats.price_history &&
                (() => {
                  const uniqueYears = new Set(stats.price_history.map((p) => Math.floor(p.year)));
                  return uniqueYears.size > 1 ? (
                    <div className="mx-3 mt-2 bg-warm-50 dark:bg-warm-800 rounded p-2">
                      <span className="text-xs text-warm-700 dark:text-warm-300">
                        {t('areaPane.priceHistory')}
                      </span>
                      <PriceHistoryChart points={stats.price_history} />
                    </div>
                  ) : null;
                })()}
              {displayFeatureGroups.map((group) => {
                const showNearbyStations =
                  hexagonLocation != null && STATION_GROUP_NAMES.has(group.name);
                const hasData = group.features.some(
                  (feature) => numericByName.has(feature.name) || enumByName.has(feature.name)
                );
                const expanded = isGroupExpanded(group.name);
                if (!hasData && !showNearbyStations && stats.count === 0) return null;

                const stackedCharts = STACKED_GROUPS[group.name];
                const stackedEnumCharts = STACKED_ENUM_GROUPS[group.name];

                const stackedEnumFeatureNames = new Set<string>(
                  stackedEnumCharts?.flatMap((c) =>
                    [c.feature, ...c.components].filter((s): s is string => Boolean(s))
                  ) ?? []
                );

                return (
                  <div key={group.name}>
                    <CollapsibleGroupHeader
                      name={group.name}
                      expanded={expanded}
                      onToggle={() => onToggleGroup(group.name)}
                      className="area-pane-group-header sticky top-0 z-10 bg-white px-3 pb-1.5 pt-4 text-[11px] font-bold uppercase tracking-wide text-warm-500 hover:bg-warm-50 dark:bg-navy-950 dark:text-warm-400 dark:hover:bg-navy-900"
                    />
                    {expanded && (
                      <div className="divide-y divide-warm-100 px-3 py-1 dark:divide-navy-800">
                        {showNearbyStations && <NearbyStationsCard location={hexagonLocation} />}
                        {stackedCharts?.map((chart) => {
                          const segments = chart.components
                            .map((name) => ({
                              name,
                              value: numericByName.get(name)?.mean ?? 0,
                            }))
                            .filter((s) => s.value > 0);

                          const isPercentageComposition = chart.unit === '%' && !chart.feature;
                          const displaySegments = isPercentageComposition
                            ? normalizePercentageSegments(segments)
                            : segments;

                          const aggregateStats = chart.feature
                            ? numericByName.get(chart.feature)
                            : undefined;
                          const total = aggregateStats
                            ? aggregateStats.mean
                            : displaySegments.reduce((sum, s) => sum + s.value, 0);

                          // Use rateFeature (e.g. per-1k) for display if available
                          const rateStats = chart.rateFeature
                            ? numericByName.get(chart.rateFeature)
                            : undefined;
                          const displayValue = isPercentageComposition
                            ? 100
                            : rateStats
                              ? rateStats.mean
                              : total;

                          // Use rateFeature for info popup and national average when available
                          const infoFeatureName = chart.rateFeature ?? chart.feature;
                          const featureMeta = infoFeatureName
                            ? globalFeatureByName.get(infoFeatureName)
                            : undefined;

                          const globalMean = featureMeta?.histogram
                            ? calculateHistogramMean(featureMeta.histogram)
                            : undefined;

                          if (total === 0) return null;

                          const crimeSeries = chart.feature
                            ? crimeByYearByFeatureName.get(chart.feature)
                            : undefined;

                          return (
                            <div
                              key={ts(chart.label)}
                              className="bg-warm-50 dark:bg-warm-800 rounded p-2"
                            >
                              <div className="flex justify-between items-baseline mb-1.5">
                                {featureMeta ? (
                                  <FeatureLabel
                                    feature={{ ...featureMeta, name: ts(chart.label) }}
                                    onShowInfo={setInfoFeature}
                                    className="mr-2"
                                    wrap
                                  />
                                ) : (
                                  <span className="mr-2 min-w-0 break-words text-xs leading-snug text-warm-700 dark:text-warm-300">
                                    {ts(chart.label)}
                                  </span>
                                )}
                                <div className="text-right shrink-0">
                                  <span className="text-xs font-semibold text-teal-700 dark:text-teal-400 whitespace-nowrap">
                                    {formatValue(displayValue)}
                                    {chart.unit ? ` ${chart.unit}` : ''}
                                  </span>
                                  {globalMean != null && (
                                    <div className="text-[10px] text-warm-400 dark:text-warm-500 whitespace-nowrap">
                                      {t('areaPane.nationalAvg')}: {formatValue(globalMean)}
                                    </div>
                                  )}
                                </div>
                              </div>
                              <StackedBarChart
                                segments={displaySegments}
                                total={total}
                                colorMap={
                                  chart.label === 'Political vote share'
                                    ? PARTY_FEATURE_COLORS
                                    : STACKED_SEGMENT_COLORS
                                }
                              />
                              {crimeSeries && crimeSeries.points.length > 1 && (
                                <div className="mt-2">
                                  <CrimeYearChart
                                    points={crimeSeries.points}
                                    latestAvailableYear={stats?.crime_latest_year}
                                  />
                                </div>
                              )}
                            </div>
                          );
                        })}
                        {(() => {
                          const stackedFeatureNames = new Set<string>(
                            stackedCharts?.flatMap((c) =>
                              [c.feature, c.rateFeature, ...c.components].filter((s): s is string =>
                                Boolean(s)
                              )
                            ) ?? []
                          );
                          return group.features
                            .filter(
                              (f) =>
                                !stackedFeatureNames.has(f.name) &&
                                !stackedEnumFeatureNames.has(f.name)
                            )
                            .map((feature) => {
                              const numericStats = numericByName.get(feature.name);
                              const enumStats = enumByName.get(feature.name);

                              if (numericStats) {
                                const globalFeature = globalFeatureByName.get(feature.name);
                                const globalHistogram = globalFeature?.histogram;
                                const globalMean = globalHistogram
                                  ? calculateHistogramMean(globalHistogram)
                                  : undefined;
                                const crimeSeries = crimeByYearByFeatureName.get(feature.name);

                                return (
                                  <MetricRow
                                    key={feature.name}
                                    label={
                                      <MetricFeatureLabel
                                        feature={feature}
                                        onShowInfo={setInfoFeature}
                                        aboutLabel={t('filters.aboutData')}
                                      />
                                    }
                                    chart={
                                      crimeSeries && crimeSeries.points.length > 1 ? (
                                        <CrimeYearChart
                                          points={crimeSeries.points}
                                          latestAvailableYear={stats?.crime_latest_year}
                                        />
                                      ) : (
                                        numericStats.histogram &&
                                        (globalHistogram ? (
                                          <DualHistogram
                                            localCounts={numericStats.histogram.counts}
                                            globalCounts={globalHistogram.counts}
                                            p1={numericStats.histogram.p1}
                                            p99={numericStats.histogram.p99}
                                            globalMean={globalMean}
                                            meanLabel={t('areaPane.nationalAvg')}
                                            formatLabel={(v) =>
                                              formatFilterValue(
                                                v,
                                                feature.suffix === '%'
                                                  ? { raw: feature.raw, suffix: feature.suffix }
                                                  : feature.raw
                                              )
                                            }
                                            integerAxisLabels={feature.step === 1}
                                            compact
                                          />
                                        ) : (
                                          <DualHistogram
                                            localCounts={numericStats.histogram.counts}
                                            globalCounts={numericStats.histogram.counts}
                                            p1={numericStats.histogram.p1}
                                            p99={numericStats.histogram.p99}
                                            formatLabel={(v) =>
                                              formatFilterValue(
                                                v,
                                                feature.suffix === '%'
                                                  ? { raw: feature.raw, suffix: feature.suffix }
                                                  : feature.raw
                                              )
                                            }
                                            integerAxisLabels={feature.step === 1}
                                            compact
                                          />
                                        ))
                                      )
                                    }
                                    value={formatValue(numericStats.mean, feature)}
                                    valueTitle={
                                      globalMean != null
                                        ? `${t('areaPane.nationalAvg')}: ${formatValue(globalMean)}`
                                        : undefined
                                    }
                                  />
                                );
                              }

                              if (enumStats) {
                                const globalFeature = globalFeatureByName.get(feature.name);
                                return (
                                  <div
                                    key={feature.name}
                                    className="bg-warm-50 dark:bg-warm-800 rounded p-2"
                                  >
                                    <FeatureLabel
                                      feature={feature}
                                      onShowInfo={setInfoFeature}
                                      wrap
                                    />
                                    <EnumBarChart
                                      counts={enumStats.counts}
                                      globalCounts={globalFeature?.counts}
                                      featureName={feature.name}
                                    />
                                  </div>
                                );
                              }

                              return null;
                            });
                        })()}
                        {stackedEnumCharts?.map((chart) => {
                          const featureMeta = chart.feature
                            ? globalFeatureByName.get(chart.feature)
                            : undefined;

                          if (chart.components.length === 1) {
                            const stats = enumByName.get(chart.components[0]);
                            if (!stats) return null;

                            const segments = chart.valueOrder
                              .map((value) => ({ name: value, value: stats.counts[value] ?? 0 }))
                              .filter((s) => s.value > 0);
                            const total = segments.reduce((sum, s) => sum + s.value, 0);
                            if (total === 0) return null;

                            return (
                              <div
                                key={ts(chart.label)}
                                className="bg-warm-50 dark:bg-warm-800 rounded p-2"
                              >
                                <div className="flex justify-between items-baseline mb-1.5">
                                  {featureMeta ? (
                                    <FeatureLabel
                                      feature={featureMeta}
                                      onShowInfo={setInfoFeature}
                                      className="mr-2"
                                      wrap
                                    />
                                  ) : (
                                    <span className="mr-2 min-w-0 break-words text-xs leading-snug text-warm-700 dark:text-warm-300">
                                      {ts(chart.label)}
                                    </span>
                                  )}
                                  <span className="text-xs font-semibold text-teal-700 dark:text-teal-400 whitespace-nowrap">
                                    {total.toLocaleString()}
                                  </span>
                                </div>
                                <StackedBarChart
                                  segments={segments}
                                  total={total}
                                  colorMap={Object.fromEntries(
                                    chart.valueOrder.map((v, i) => [v, chart.valueColors[i]])
                                  )}
                                />
                              </div>
                            );
                          }

                          const components = chart.components
                            .map((name) => {
                              const stats = enumByName.get(name);
                              return stats ? { label: name, stats } : null;
                            })
                            .filter((c): c is NonNullable<typeof c> => c !== null);

                          if (components.length === 0) return null;

                          return (
                            <div
                              key={ts(chart.label)}
                              className="bg-warm-50 dark:bg-warm-800 rounded p-2"
                            >
                              <div className="mb-1.5">
                                {featureMeta ? (
                                  <FeatureLabel
                                    feature={{ ...featureMeta, name: ts(chart.label) }}
                                    onShowInfo={setInfoFeature}
                                    wrap
                                  />
                                ) : (
                                  <span className="text-xs text-warm-700 dark:text-warm-300">
                                    {ts(chart.label)}
                                  </span>
                                )}
                              </div>
                              <StackedEnumChart
                                components={components}
                                valueOrder={chart.valueOrder}
                                valueColors={chart.valueColors}
                              />
                            </div>
                          );
                        })}
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          ) : null}
        </div>
      </div>

      {infoFeature && (
        <FeatureInfoPopup
          feature={infoFeature}
          onClose={() => setInfoFeature(null)}
          onNavigateToSource={onNavigateToSource}
        />
      )}
    </>
  );
}
