import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ts } from '../../i18n/server';
import type {
  CrimeAreaAverage,
  CrimeRecordsResponse,
  CrimeYearStats,
  FeatureMeta,
  HexagonStatsResponse,
  NumericFeatureStats,
} from '../../types';
import { STACKED_GROUPS, STACKED_SEGMENT_COLORS } from '../../lib/consts';
import { formatValue, calculateHistogramMean } from '../../lib/format';
import { FeatureLabel } from '../ui/FeatureLabel';
import StackedBarChart from './StackedBarChart';
import NumberLine, { type NumberLinePoint } from './NumberLine';
import CrimeYearChart from './CrimeYearChart';
import CrimeRecordsSection from './CrimeRecordsSection';

type CrimeWindow = '7y' | '2y';

/** Strip the " (/yr, 7y|2y)" suffix to the bare crime type. */
function bareType(featureName: string): string {
  return featureName.replace(/ \(\/yr, \dy\)$/, '');
}
/** Re-target a crime-feature name at the chosen averaging window. */
function toWindow(featureName: string, window: CrimeWindow): string {
  return featureName.replace(/(\/yr, )\dy/, `$1${window}`);
}

/**
 * Segment colours keyed by the BARE crime type. The stacked bar then labels each
 * segment with the clean crime name (e.g. "Burglary") and keeps one stable colour
 * regardless of whether the 7-year or 2-year window is selected.
 */
const SEGMENT_COLOR_BY_BARE: Record<string, string> = Object.fromEntries(
  Object.entries(STACKED_SEGMENT_COLORS).map(([name, color]) => [bareType(name), color])
);

interface CrimeGroupBodyProps {
  stats: HexagonStatsResponse;
  numericByName: Map<string, NumericFeatureStats>;
  /** Keyed by the FULL crime-feature name (e.g. "Burglary (/yr, 7y)"). */
  crimeAreaAvgByName: Map<string, CrimeAreaAverage>;
  /** Keyed by the bare crime type (e.g. "Burglary"). */
  crimeByYearByType: Map<string, CrimeYearStats>;
  globalFeatureByName: Map<string, FeatureMeta>;
  onShowInfo: (feature: FeatureMeta) => void;
  onLoadCrimeRecords?: (offset: number) => Promise<CrimeRecordsResponse>;
  selectionKey: string | null;
}

export default function CrimeGroupBody({
  stats,
  numericByName,
  crimeAreaAvgByName,
  crimeByYearByType,
  globalFeatureByName,
  onShowInfo,
  onLoadCrimeRecords,
  selectionKey,
}: CrimeGroupBodyProps) {
  const { t } = useTranslation();
  // One selector drives every value and comparison in this group.
  const [crimeWindow, setCrimeWindow] = useState<CrimeWindow>('7y');

  const fmtCount = (value?: number) => (value == null ? '—' : formatValue(value));

  const rollupCards = STACKED_GROUPS.Crime ?? [];

  return (
    <>
      <div className="flex items-center justify-between gap-2 py-2">
        <span className="text-[11px] font-semibold uppercase tracking-wide text-warm-400 dark:text-warm-500">
          {t('areaPane.crimeWindowLabel')}
        </span>
        <div className="grid grid-cols-2 gap-0.5 rounded-md bg-warm-200 p-0.5 dark:bg-navy-800">
          {(['7y', '2y'] as const).map((w) => {
            const active = crimeWindow === w;
            return (
              <button
                key={w}
                type="button"
                aria-pressed={active}
                onClick={() => setCrimeWindow(w)}
                className={`rounded px-3 py-1 text-xs font-medium ${
                  active
                    ? '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(w === '7y' ? 'areaPane.crimeWindow7y' : 'areaPane.crimeWindow2y')}
              </button>
            );
          })}
        </div>
      </div>

      {rollupCards.map((card) => {
        if (!card.feature) return null;
        const bare = bareType(card.feature);
        const windowFeature = toWindow(card.feature, crimeWindow);
        const rate = numericByName.get(windowFeature)?.mean;

        const segments = card.components
          .map((name) => ({
            name: bareType(name),
            value: numericByName.get(toWindow(name, crimeWindow))?.mean ?? 0,
          }))
          .filter((s) => s.value > 0);
        const total = segments.reduce((sum, s) => sum + s.value, 0);
        if (rate == null && total === 0) return null;

        const displayValue = rate ?? total;
        // Info popup / icon stay anchored to the 7-year feature so the metric
        // definition is stable across windows; only the numbers change.
        const featureMeta = globalFeatureByName.get(card.feature);
        const globalMean = featureMeta?.histogram
          ? calculateHistogramMean(featureMeta.histogram)
          : undefined;
        const crimeAreaAvg = crimeAreaAvgByName.get(windowFeature);
        const nationalAvg = crimeAreaAvg?.national ?? globalMean;
        const cardTitle = t('areaPane.crimeCardTitle', { name: ts(card.label) });

        const numberLinePoints: NumberLinePoint[] = crimeAreaAvg
          ? (
              [
                { kind: 'area', label: t('areaPane.thisArea'), value: displayValue },
                nationalAvg != null
                  ? { kind: 'national', label: t('areaPane.national'), value: nationalAvg }
                  : null,
                crimeAreaAvg.outcode != null
                  ? {
                      kind: 'outcode',
                      label: stats.crime_outcode ?? t('areaPane.outcodeAvg'),
                      value: crimeAreaAvg.outcode,
                    }
                  : null,
                crimeAreaAvg.sector != null
                  ? {
                      kind: 'sector',
                      label: stats.crime_sector ?? t('areaPane.sectorAvg'),
                      value: crimeAreaAvg.sector,
                    }
                  : null,
              ] as (NumberLinePoint | null)[]
            ).filter((p): p is NumberLinePoint => p !== null)
          : [];

        const series = crimeByYearByType.get(bare);

        return (
          <div key={card.label} className="rounded bg-warm-50 p-2 dark:bg-warm-800">
            <div className="mb-1.5 flex items-baseline justify-between gap-2">
              {featureMeta ? (
                <FeatureLabel
                  feature={{ ...featureMeta, name: ts(card.label) }}
                  label={cardTitle}
                  onShowInfo={onShowInfo}
                  className="mr-2"
                  wrap
                />
              ) : (
                <span className="mr-2 min-w-0 break-words text-xs leading-snug text-warm-700 dark:text-warm-300">
                  {cardTitle}
                </span>
              )}
              <div className="shrink-0 text-right">
                <span className="whitespace-nowrap text-xs font-semibold text-teal-700 dark:text-teal-400">
                  {fmtCount(displayValue)}
                </span>
              </div>
            </div>

            {total > 0 && (
              <StackedBarChart segments={segments} total={total} colorMap={SEGMENT_COLOR_BY_BARE} />
            )}
            {numberLinePoints.length >= 2 && (
              <div className="mt-2">
                <NumberLine points={numberLinePoints} format={formatValue} />
              </div>
            )}
            {series && series.points.length > 1 && (
              <div className="mt-2">
                <CrimeYearChart
                  points={series.points}
                  latestAvailableYear={stats.crime_latest_year}
                />
              </div>
            )}
          </div>
        );
      })}

      {onLoadCrimeRecords && (
        <CrimeRecordsSection
          selectionKey={selectionKey}
          total={stats.crime_total_records}
          onLoad={onLoadCrimeRecords}
        />
      )}
    </>
  );
}
