import { useState, useEffect } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { useProfile } from '../context/ProfileContext'
import {
  LineChart, Line, BarChart, Bar,
  XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid,
  ReferenceLine, PieChart, Pie, Cell, ComposedChart,
  ScatterChart, Scatter,
} from 'recharts'
import { ChevronRight, Brain, ChevronDown, ChevronUp, Trash2 } from 'lucide-react'
import { api } from '../utils/api'
import { photoMonthKey, photoSortKey, formatPhotoCaption } from '../utils/photoDisplay'
import { getBfCategory } from '../utils/calc'
import { getStatusColor, getStatusBg } from '../utils/interpret'
import { MACRO_CHART, macroFillByName, NUTRITION_MACRO_CHART_BLOCK_PX } from '../utils/macroChartTheme'
import Markdown from '../utils/Markdown'
import FitnessDashboardOverview from '../components/FitnessDashboardOverview'
import NutritionCharts, { WeeklyMacroDistributionPanel } from '../components/NutritionCharts'
import RecoveryDashboardOverview from '../components/RecoveryDashboardOverview'
import KpiTilesOverview from '../components/KpiTilesOverview'
import dayjs from 'dayjs'
import 'dayjs/locale/de'
dayjs.locale('de')

function rollingAvg(arr, key, window=7) {
  return arr.map((d,i) => {
    const s = arr.slice(Math.max(0,i-window+1),i+1).map(x=>x[key]).filter(v=>v!=null)
    return s.length ? {...d,[`${key}_avg`]:Math.round(s.reduce((a,b)=>a+b)/s.length*10)/10} : d
  })
}
const fmtDate = d => dayjs(d).format('DD.MM')

function NavToCaliper() {
  const nav = useNavigate()
  return <button className="btn btn-secondary" style={{fontSize:10,padding:'2px 8px'}}
    onClick={()=>nav('/caliper')}>Caliper-Daten <ChevronRight size={10}/></button>
}
function NavToCircum() {
  const nav = useNavigate()
  return <button className="btn btn-secondary" style={{fontSize:10,padding:'2px 8px'}}
    onClick={()=>nav('/circum')}>Umfang-Daten <ChevronRight size={10}/></button>
}
function EmptySection({ text, to, toLabel }) {
  const nav = useNavigate()
  return (
    <div style={{padding:32,textAlign:'center',color:'var(--text3)',fontSize:13}}>
      <div style={{marginBottom:12}}>{text}</div>
      {to && <button className="btn btn-primary" onClick={()=>nav(to)}>{toLabel||'Daten erfassen'}</button>}
    </div>
  )
}

function SectionHeader({ title, to, toLabel, lastUpdated }) {
  const nav = useNavigate()
  return (
    <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14}}>
      <h2 style={{fontSize:17,fontWeight:700,margin:0}}>{title}</h2>
      <div style={{display:'flex',gap:8,alignItems:'center'}}>
        {lastUpdated && <span style={{fontSize:11,color:'var(--text3)'}}>{dayjs(lastUpdated).format('DD.MM.YY')}</span>}
        {to && (
          <button className="btn btn-secondary" style={{fontSize:12,padding:'5px 10px'}} onClick={()=>nav(to)}>
            {toLabel||'Daten'} <ChevronRight size={12}/>
          </button>
        )}
      </div>
    </div>
  )
}

function RuleCard({ item }) {
  const [open, setOpen] = useState(false)
  const color = getStatusColor(item.status)
  return (
    <div style={{border:`1px solid ${color}33`,borderRadius:8,marginBottom:6,overflow:'hidden'}}>
      <div style={{display:'flex',alignItems:'center',gap:8,padding:'8px 12px',
        background:getStatusBg(item.status)+'88',cursor:'pointer'}} onClick={()=>setOpen(o=>!o)}>
        <span style={{fontSize:15}}>{item.icon}</span>
        <div style={{flex:1}}>
          <div style={{fontSize:11,fontWeight:600,color,textTransform:'uppercase',letterSpacing:'0.04em'}}>{item.category}</div>
          <div style={{fontSize:13,fontWeight:500}}>{item.title}</div>
        </div>
        {item.value && <span style={{fontSize:14,fontWeight:700,color}}>{item.value}</span>}
        {open ? <ChevronUp size={14} color="var(--text3)"/> : <ChevronDown size={14} color="var(--text3)"/>}
      </div>
      {open && <div style={{padding:'8px 12px',fontSize:12,color:'var(--text2)',lineHeight:1.6,
        borderTop:`1px solid ${color}22`}}>{item.detail}</div>}
    </div>
  )
}

function verdictShort(status) {
  if (status === 'good') return 'Gut'
  if (status === 'warn') return 'Hinweis'
  return 'Achtung'
}

/** Eine KPI-Kachelzeile aus Summary + Interpretationsregeln (ohne Duplikate zur reinen Bewertungsliste). */
function buildBodyKpiTiles({
  summary, rules, trendPeriods, minW, maxW, avgAll, dataPoints, sex, bfCat, goalW,
}) {
  const tiles = []

  if (summary.weight_kg != null) {
    const t90 = trendPeriods.find(t => t.label === '90T')
    const t30 = trendPeriods.find(t => t.label === '30T')
    const d = t90?.diff_kg ?? t30?.diff_kg ?? trendPeriods[0]?.diff_kg
    let st = 'good'
    let vs = 'Stabil'
    if (d != null) {
      if (d < -0.25) { st = 'good'; vs = 'Trend ↓' }
      else if (d > 0.25) { st = 'warn'; vs = 'Trend ↑' }
      else { st = 'good'; vs = 'Stabil' }
    }
    const trendBits = trendPeriods.length
      ? trendPeriods.map(t => `${t.label} ${t.diff_kg > 0 ? '+' : ''}${t.diff_kg} kg`).join(' · ')
      : ''
    const hoverBody = [
      'Gewicht im gewählten Zeitraum (letzter Messwert).',
      avgAll != null ? `Durchschnitt: ${avgAll} kg` : null,
      minW != null && maxW != null ? `Min. / Max.: ${minW} – ${maxW} kg` : null,
      trendBits ? `Änderung: ${trendBits}` : null,
      goalW != null ? `Profil-Zielgewicht: ${goalW} kg` : null,
    ].filter(Boolean).join('\n')

    tiles.push({
      key: 'weight',
      category: 'Gewicht',
      icon: '⚖️',
      value: `${summary.weight_kg} kg`,
      sublabel: dataPoints ? `${dataPoints} Messwerte` : '',
      verdict: vs,
      status: st,
      hoverTop: 'Gewicht',
      hoverBody,
      keys: ['weight_aktuell', 'weight_trend'],
    })
  }

  const kfRule = rules.find(r => r.category === 'Körperfett')
  if (summary.body_fat_pct != null) {
    tiles.push({
      key: 'bf',
      category: 'Körperfett',
      icon: '🫧',
      value: `${summary.body_fat_pct}%`,
      valueColor: bfCat?.color,
      sublabel: bfCat?.label || summary.bf_category_label || '',
      verdict: verdictShort(kfRule?.status || 'good'),
      status: kfRule?.status || 'good',
      hoverTop: kfRule?.title || 'Körperfettanteil',
      hoverBody: [kfRule?.detail, kfRule?.related_placeholder_keys?.length ? `Registry: ${kfRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  const mmRule = rules.find(r => r.category === 'Muskelmasse')
  if (summary.lean_mass_kg != null || summary.ffmi != null) {
    const valParts = []
    if (summary.lean_mass_kg != null) valParts.push(`${summary.lean_mass_kg} kg`)
    if (summary.ffmi != null) valParts.push(`FFMI ${summary.ffmi}`)
    tiles.push({
      key: 'lean_ffmi',
      category: 'Magermasse',
      icon: '💪',
      value: valParts.join(' · ') || '—',
      sublabel: 'Lean / FFMI',
      verdict: mmRule ? verdictShort(mmRule.status) : '—',
      status: mmRule?.status || 'good',
      hoverTop: mmRule?.title || 'Muskelmasse',
      hoverBody: [mmRule?.detail, mmRule?.related_placeholder_keys?.length ? `Registry: ${mmRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  const bmiRule = rules.find(r => r.category === 'BMI')
  if (bmiRule) {
    tiles.push({
      key: 'bmi',
      category: 'BMI',
      icon: '📋',
      value: bmiRule.value || '—',
      sublabel: 'Body-Mass-Index',
      verdict: verdictShort(bmiRule.status),
      status: bmiRule.status,
      hoverTop: bmiRule.title,
      hoverBody: [bmiRule.detail, bmiRule.related_placeholder_keys?.length ? `Registry: ${bmiRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  const whrRule = rules.find(r => r.category === 'Fettverteilung')
  if (summary.whr != null) {
    const ok = summary.whr < (sex === 'm' ? 0.9 : 0.85)
    tiles.push({
      key: 'whr',
      category: 'Fettverteilung',
      icon: '📐',
      value: String(summary.whr),
      sublabel: 'WHR · Taille ÷ Hüfte',
      verdict: whrRule ? verdictShort(whrRule.status) : (ok ? 'Gut' : 'Hinweis'),
      status: whrRule?.status || (ok ? 'good' : 'warn'),
      hoverTop: whrRule?.title || 'Waist-Hip-Ratio',
      hoverBody: [whrRule?.detail, !whrRule && `Ziel unter ${sex === 'm' ? '0,90' : '0,85'}.`, whrRule?.related_placeholder_keys?.length ? `Registry: ${whrRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  const whtrRule = rules.find(r => r.category === 'Taille/Größe')
  if (summary.whtr != null) {
    const ok = summary.whtr < 0.5
    tiles.push({
      key: 'whtr',
      category: 'Taille/Größe',
      icon: '📏',
      value: String(summary.whtr),
      sublabel: 'WHtR · Taille ÷ Größe',
      verdict: whtrRule ? verdictShort(whtrRule.status) : (ok ? 'Gut' : 'Hinweis'),
      status: whtrRule?.status || (ok ? 'good' : 'warn'),
      hoverTop: whtrRule?.title || 'Waist-to-Height-Ratio',
      hoverBody: [whtrRule?.detail, !whtrRule && 'Ziel unter 0,50 (WHO).', whtrRule?.related_placeholder_keys?.length ? `Registry: ${whtrRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  const lastRule = rules.find(r => r.category.startsWith('Seit letzter'))
  if (lastRule) {
    tiles.push({
      key: 'delta',
      category: 'Messvergleich',
      icon: '📊',
      value: lastRule.value || '—',
      sublabel: 'seit Vorperiode',
      verdict: verdictShort(lastRule.status),
      status: lastRule.status,
      hoverTop: lastRule.title,
      hoverBody: [lastRule.detail, lastRule.related_placeholder_keys?.length ? `Registry: ${lastRule.related_placeholder_keys.join(', ')}` : ''].filter(Boolean).join('\n\n'),
    })
  }

  return tiles
}

function NutritionGoalsStrip({ grouped }) {
  const nav = useNavigate()
  const goals = (grouped?.nutrition || []).filter(g => g.status === 'active').slice(0, 4)
  if (!goals.length) return null
  return (
    <div className="card" style={{ marginBottom: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Ernährungsbezogene Ziele</div>
        <button type="button" className="btn btn-secondary" style={{ fontSize: 10, padding: '2px 8px' }} onClick={() => nav('/goals')}>
          Ziele <ChevronRight size={10} />
        </button>
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        {goals.map(g => (
          <div
            key={g.id}
            style={{
              flex: '1 1 140px',
              background: 'var(--surface2)',
              borderRadius: 8,
              padding: '8px 10px',
              borderTop: `3px solid ${g.is_primary ? 'var(--accent)' : 'var(--border2)'}`,
            }}
          >
            <div style={{
              fontSize: 11, fontWeight: 600, color: 'var(--text2)',
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
            }}>{g.name || g.label_de || g.goal_type}</div>
            <div style={{ marginTop: 4, height: 6, background: 'var(--border)', borderRadius: 3, overflow: 'hidden' }}>
              <div style={{
                width: `${Math.min(100, Math.max(0, g.progress_pct ?? 0))}%`,
                height: '100%',
                background: 'var(--accent)',
              }} />
            </div>
            <div style={{ fontSize: 10, color: 'var(--text3)', marginTop: 4 }}>
              {Math.round(g.progress_pct ?? 0)}% · Ziel {g.target_value}{g.unit ? ` ${g.unit}` : ''}
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

function BodyGoalsStrip({ grouped }) {
  const nav = useNavigate()
  const goals = (grouped?.body || []).filter(g => g.status === 'active').slice(0, 4)
  if (!goals.length) return null
  return (
    <div className="card" style={{ marginBottom: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Körperbezogene Ziele</div>
        <button type="button" className="btn btn-secondary" style={{ fontSize: 10, padding: '2px 8px' }} onClick={() => nav('/goals')}>
          Ziele <ChevronRight size={10} />
        </button>
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
        {goals.map(g => (
          <div
            key={g.id}
            style={{
              flex: '1 1 140px',
              background: 'var(--surface2)',
              borderRadius: 8,
              padding: '8px 10px',
              borderTop: `3px solid ${g.is_primary ? 'var(--accent)' : 'var(--border2)'}`,
            }}
          >
            <div style={{
              fontSize: 11, fontWeight: 600, color: 'var(--text2)',
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
            }}>{g.name || g.label_de || g.goal_type}</div>
            <div style={{ marginTop: 4, height: 6, background: 'var(--border)', borderRadius: 3, overflow: 'hidden' }}>
              <div style={{
                width: `${Math.min(100, Math.max(0, g.progress_pct ?? 0))}%`,
                height: '100%',
                background: 'var(--accent)',
              }} />
            </div>
            <div style={{ fontSize: 10, color: 'var(--text3)', marginTop: 4 }}>
              {Math.round(g.progress_pct ?? 0)}% · Ziel {g.target_value}{g.unit ? ` ${g.unit}` : ''}
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

function InsightBox({ insights, slugs, onRequest, loading }) {
  const [expanded, setExpanded] = useState(null)
  const relevant = insights?.filter(i=>slugs.includes(i.scope))||[]
  const LABELS = {gesamt:'Gesamt',koerper:'Komposition',ernaehrung:'Ernährung',
    aktivitaet:'Fitness',gesundheit:'Gesundheit',ziele:'Ziele',
    pipeline:'🔬 Mehrstufige Analyse',
    pipeline_body:'Pipeline Körper',pipeline_nutrition:'Pipeline Ernährung',
    pipeline_activity:'Pipeline Fitness',pipeline_synthesis:'Pipeline Synthese',
    pipeline_goals:'Pipeline Ziele'}
  return (
    <div style={{marginTop:14}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:8}}>
        <div style={{fontSize:12,fontWeight:600,color:'var(--text3)'}}>🤖 KI-AUSWERTUNGEN</div>
        <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
          {slugs.map(slug=>(
            <button key={slug} className="btn btn-secondary" style={{fontSize:11,padding:'4px 8px'}}
              onClick={()=>onRequest(slug)} disabled={loading===slug}>
              {loading===slug
                ? <div className="spinner" style={{width:11,height:11}}/>
                : <><Brain size={11}/> {LABELS[slug]||slug}</>}
            </button>
          ))}
        </div>
      </div>
      {relevant.length===0 && (
        <div style={{padding:'10px 12px',background:'var(--surface2)',borderRadius:8,
          fontSize:12,color:'var(--text3)'}}>
          Noch keine Auswertung. Klicke oben um eine zu erstellen.
        </div>
      )}
      {relevant.map(ins=>(
        <div key={ins.id} style={{marginBottom:8,border:'1px solid var(--accent)33',borderRadius:10,overflow:'hidden'}}>
          <div style={{display:'flex',alignItems:'center',gap:8,padding:'8px 12px',
            background:'var(--accent-light)66',cursor:'pointer'}}
            onClick={()=>setExpanded(expanded===ins.id?null:ins.id)}>
            <div style={{flex:1,fontSize:11,color:'var(--accent)',fontWeight:600}}>
              {dayjs(ins.created).format('DD. MMM YYYY, HH:mm')} · {LABELS[ins.scope]||ins.scope}
            </div>
            {expanded===ins.id?<ChevronUp size={14}/>:<ChevronDown size={14}/>}
          </div>
          {expanded===ins.id && <div style={{padding:'12px 14px'}}><Markdown text={ins.content}/></div>}
        </div>
      ))}
    </div>
  )
}

// ── Period selector ───────────────────────────────────────────────────────────
function PeriodSelector({ value, onChange }) {
  const opts = [{v:30,l:'30 Tage'},{v:90,l:'90 Tage'},{v:180,l:'6 Monate'},{v:365,l:'1 Jahr'},{v:9999,l:'Alles'}]
  return (
    <div style={{display:'flex',gap:4,marginBottom:12}}>
      {opts.map(o=>(
        <button key={o.v} onClick={()=>onChange(o.v)}
          style={{padding:'4px 10px',borderRadius:12,fontSize:11,fontWeight:500,border:'1.5px solid',
            cursor:'pointer',fontFamily:'var(--font)',
            background:value===o.v?'var(--accent)':'transparent',
            borderColor:value===o.v?'var(--accent)':'var(--border2)',
            color:value===o.v?'white':'var(--text2)'}}>
          {o.l}
        </button>
      ))}
    </div>
  )
}

// ── Body Section — Layer 2b: Daten nur aus GET /api/charts/body-history-viz ──
function BodySection({ profile, insights, onRequest, loadingSlug, filterActiveSlugs }) {
  const [period, setPeriod] = useState(90)
  const [groupedGoals, setGroupedGoals] = useState(null)
  const [viz, setViz] = useState(null)
  const [vizLoading, setVizLoading] = useState(true)
  const [vizError, setVizError] = useState(null)

  const sex = profile?.sex || 'm'

  useEffect(() => {
    let cancelled = false
    api.listGoalsGrouped()
      .then(g => { if (!cancelled) setGroupedGoals(g) })
      .catch(() => { if (!cancelled) setGroupedGoals({}) })
    return () => { cancelled = true }
  }, [])

  useEffect(() => {
    let cancelled = false
    setVizLoading(true)
    setVizError(null)
    api.getBodyHistoryViz(period)
      .then(data => {
        if (!cancelled) {
          setViz(data)
          setVizLoading(false)
        }
      })
      .catch(e => {
        if (!cancelled) {
          setVizError(e.message || 'Laden fehlgeschlagen')
          setVizLoading(false)
        }
      })
    return () => { cancelled = true }
  }, [period])

  const w = viz?.weight
  const cal = viz?.caliper
  const circ = viz?.circumference
  const summary = viz?.summary || {}

  const wCd = (w?.series || []).map(row => ({
    date: fmtDate(row.date),
    weight: row.weight,
    avg7: row.avg7,
    avg14: row.avg14,
  }))
  const hasWeight = (w?.data_points || 0) >= 2
  const avgAll = w?.overall_avg_kg
  const minW = w?.min_kg
  const maxW = w?.max_kg
  const trendPeriods = w?.trend_periods || []

  const bfCd = (cal?.series || []).map(s => ({
    date: fmtDate(s.date),
    bf: s.body_fat_pct,
  }))

  const propChartData = (circ?.proportion_series || []).map(p => ({
    date: fmtDate(p.date),
    vTaper: p.v_taper_cm,
    vTaper_avg: p.v_taper_cm_avg,
    belly: p.belly_cm,
  }))
  const showBellyOnProp = propChartData.some(d => d.belly != null && d.belly !== undefined)

  const idxSeriesRaw = circ?.index_series || []
  const idxSeries = idxSeriesRaw.map(row => ({ ...row, date: fmtDate(row.date) }))
  const idxOk = circ?.index_usable

  const cirCd = (circ?.fallback_multiline || []).map(r => ({
    date: fmtDate(r.date),
    waist: r.waist,
    hip: r.hip,
    belly: r.belly,
  }))

  const bfCat = summary.body_fat_pct != null ? getBfCategory(summary.body_fat_pct, sex) : null
  const goalW = viz?.profile?.goal_weight_kg ?? profile?.goal_weight
  const goalBf = viz?.profile?.goal_bf_pct ?? profile?.goal_bf_pct

  const rules = (viz?.interpretation_tiles || []).map(t => ({
    category: t.category,
    icon: t.icon,
    status: t.status,
    title: t.title,
    detail: t.detail,
    value: t.value,
    related_placeholder_keys: t.related_placeholder_keys,
  }))

  const kpiTiles = buildBodyKpiTiles({
    summary,
    rules,
    trendPeriods,
    minW,
    maxW,
    avgAll,
    dataPoints: w?.data_points,
    sex,
    bfCat,
    goalW,
  })

  const hasAnyData =
    (w?.data_points > 0) ||
    (cal?.data_points > 0) ||
    (cirCd.length > 0)

  if (vizLoading && !viz) {
    return (
      <div>
        <SectionHeader title="⚖️ Körper" />
        <div className="empty-state"><div className="spinner" /></div>
      </div>
    )
  }
  if (vizError) {
    return (
      <div>
        <SectionHeader title="⚖️ Körper" />
        <div style={{ padding: 16, color: 'var(--danger)' }}>{vizError}</div>
      </div>
    )
  }
  if (!hasAnyData) {
    return (
      <div>
        <SectionHeader title="⚖️ Körper" />
        <PeriodSelector value={period} onChange={setPeriod} />
        <EmptySection text="Noch keine Körperdaten im gewählten Zeitraum." to="/weight" toLabel="Gewicht eintragen" />
      </div>
    )
  }

  return (
    <div>
      <SectionHeader title="⚖️ Körper" lastUpdated={viz?.last_updated} />
      <PeriodSelector value={period} onChange={setPeriod} />

      <BodyGoalsStrip grouped={groupedGoals} />

      <p style={{ fontSize: 11, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 10 }}>
        Daten und Kennzahlen aus dem Backend-Bundle (gleiche Quelle wie Platzhalter). Training: <strong>Verlauf → Fitness</strong>.
      </p>

      {viz?.meta?.layer_2a_alignment && (
        <div style={{ fontSize: 10, color: 'var(--text3)', marginBottom: 8, lineHeight: 1.4 }}>
          {viz.meta.layer_2a_alignment}
        </div>
      )}

      <KpiTilesOverview tiles={kpiTiles} />

      {vizLoading && (
        <div style={{ fontSize: 11, color: 'var(--text3)', marginBottom: 8 }}>Aktualisiere…</div>
      )}

      {hasWeight && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>
              Gewicht · {w?.data_points || 0} Einträge
            </div>
            <button type="button" className="btn btn-secondary" style={{ fontSize: 10, padding: '2px 8px' }} onClick={() => { window.location.href = '/weight' }}>
              Daten <ChevronRight size={10} />
            </button>
          </div>
          <ResponsiveContainer width="100%" height={200}>
            <LineChart data={wCd} margin={{ top: 4, right: 8, bottom: 0, left: -20 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} interval={Math.max(0, Math.floor(wCd.length / 6) - 1)} />
              <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              {avgAll != null && (
                <ReferenceLine y={avgAll} stroke="var(--text3)" strokeDasharray="4 4" strokeWidth={1} label={{ value: `Ø ${avgAll}`, fontSize: 9, fill: 'var(--text3)', position: 'right' }} />
              )}
              {goalW != null && (
                <ReferenceLine y={goalW} stroke="var(--accent)" strokeDasharray="5 3" strokeWidth={1.5} label={{ value: `Ziel ${goalW}kg`, fontSize: 9, fill: 'var(--accent)', position: 'right' }} />
              )}
              <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }} formatter={(v, n) => [`${v} kg`, n === 'weight' ? 'Täglich' : n === 'avg7' ? 'Ø 7 Tage' : 'Ø 14 Tage']} />
              <Line type="monotone" dataKey="weight" stroke="#378ADD88" strokeWidth={1.5} dot={{ r: 3, fill: '#378ADD', stroke: '#378ADD', strokeWidth: 1 }} activeDot={{ r: 5 }} name="weight" />
              <Line type="monotone" dataKey="avg7" stroke="#378ADD" strokeWidth={2.5} dot={false} name="avg7" />
              <Line type="monotone" dataKey="avg14" stroke="#1D9E75" strokeWidth={2} dot={false} strokeDasharray="6 3" name="avg14" />
            </LineChart>
          </ResponsiveContainer>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 6, fontSize: 10, color: 'var(--text3)' }}>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#378ADD88', verticalAlign: 'middle', marginRight: 3 }} />● Täglich</span>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#378ADD', verticalAlign: 'middle', marginRight: 3 }} />Ø 7T</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}><svg width="14" height="4"><line x1="0" y1="2" x2="14" y2="2" stroke="#1D9E75" strokeWidth="2" strokeDasharray="5 3" /></svg>Ø 14T</span>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: 'var(--text3)', verticalAlign: 'middle', marginRight: 3, borderTop: '2px dashed' }} />Ø Gesamt</span>
          </div>
        </div>
      )}

      {bfCd.length >= 2 && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Körperfett (Caliper)</div>
            <NavToCaliper />
          </div>
          <ResponsiveContainer width="100%" height={170}>
            <LineChart data={bfCd} margin={{ top: 4, right: 8, bottom: 0, left: -20 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }} formatter={v => [`${v}%`, 'KF%']} />
              {goalBf != null && <ReferenceLine y={goalBf} stroke="#D85A30" strokeDasharray="5 3" strokeWidth={1.5} label={{ value: `Ziel ${goalBf}%`, fontSize: 9, fill: '#D85A30', position: 'right' }} />}
              <Line type="monotone" dataKey="bf" stroke="#D85A30" strokeWidth={2.5} dot={{ r: 4, fill: '#D85A30' }} name="bf" />
            </LineChart>
          </ResponsiveContainer>
          <div style={{ fontSize: 10, color: 'var(--text3)', marginTop: 6, lineHeight: 1.4 }}>Magermasse aus Gewicht und KF% — zweite Kurve entfällt.</div>
        </div>
      )}

      {propChartData.length >= 2 && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8, gap: 8 }}>
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Silhouette & Proportion</div>
              <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginTop: 4 }}>
                <strong>V-Taper (Brust − Taille)</strong> in cm.
                {showBellyOnProp && <><strong> Bauch</strong> (rechte Achse).</>}
              </div>
            </div>
            <NavToCircum />
          </div>
          <ResponsiveContainer width="100%" height={200}>
            <ComposedChart data={propChartData} margin={{ top: 4, right: showBellyOnProp ? 4 : 8, bottom: 0, left: -20 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <YAxis yAxisId="taper" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              {showBellyOnProp && <YAxis yAxisId="belly" orientation="right" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />}
              <Tooltip
                contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }}
                formatter={(v, name) => {
                  if (name === 'vTaper' || name === 'vTaper_avg') return [`${v} cm`, name === 'vTaper_avg' ? 'Ø V-Taper (3 Messungen)' : 'Brust − Taille']
                  if (name === 'belly') return [`${v} cm`, 'Bauch']
                  return [v, name]
                }}
              />
              <Line yAxisId="taper" type="monotone" dataKey="vTaper" stroke="#1D9E75" strokeWidth={2} dot={{ r: 3 }} name="vTaper" />
              <Line yAxisId="taper" type="monotone" dataKey="vTaper_avg" stroke="#1D9E75" strokeWidth={1.5} strokeDasharray="5 4" dot={false} name="vTaper_avg" />
              {showBellyOnProp && <Line yAxisId="belly" type="monotone" dataKey="belly" stroke="#D4537E" strokeWidth={2} dot={{ r: 3 }} connectNulls name="belly" />}
            </ComposedChart>
          </ResponsiveContainer>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 6, fontSize: 10, color: 'var(--text3)', flexWrap: 'wrap' }}>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#1D9E75', verticalAlign: 'middle', marginRight: 3 }} />Brust − Taille</span>
            <span><span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}><svg width="14" height="4"><line x1="0" y1="2" x2="14" y2="2" stroke="#1D9E75" strokeWidth="2" strokeDasharray="5 4" /></svg></span>gleitender Mittelwert</span>
            {showBellyOnProp && <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#D4537E', verticalAlign: 'middle', marginRight: 3 }} />Bauch (cm)</span>}
          </div>
        </div>
      )}

      {idxOk && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Relative Entwicklung der Umfänge</div>
              <div style={{ fontSize: 10, color: 'var(--text3)', marginTop: 4, lineHeight: 1.4 }}>Index 100 = erste Messung im Zeitraum.</div>
            </div>
            <NavToCircum />
          </div>
          <ResponsiveContainer width="100%" height={180}>
            <LineChart data={idxSeries} margin={{ top: 4, right: 8, bottom: 0, left: -20 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              <ReferenceLine y={100} stroke="var(--text3)" strokeDasharray="4 4" strokeWidth={1} />
              <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }} formatter={(v, n) => [`${v} Index`, n === 'chest_idx' ? 'Brust' : n === 'waist_idx' ? 'Taille' : 'Bauch']} />
              {idxSeries.some(d => d.chest_idx != null) && <Line type="monotone" dataKey="chest_idx" stroke="#1D9E75" strokeWidth={2} dot={{ r: 2 }} connectNulls name="chest_idx" />}
              {idxSeries.some(d => d.waist_idx != null) && <Line type="monotone" dataKey="waist_idx" stroke="#EF9F27" strokeWidth={2} dot={{ r: 2 }} connectNulls name="waist_idx" />}
              {idxSeries.some(d => d.belly_idx != null) && <Line type="monotone" dataKey="belly_idx" stroke="#D4537E" strokeWidth={2} dot={{ r: 2 }} connectNulls name="belly_idx" />}
            </LineChart>
          </ResponsiveContainer>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 6, fontSize: 10, color: 'var(--text3)', flexWrap: 'wrap' }}>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#1D9E75', verticalAlign: 'middle', marginRight: 3 }} />Brust</span>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#EF9F27', verticalAlign: 'middle', marginRight: 3 }} />Taille</span>
            <span><span style={{ display: 'inline-block', width: 12, height: 2, background: '#D4537E', verticalAlign: 'middle', marginRight: 3 }} />Bauch</span>
          </div>
        </div>
      )}

      {propChartData.length < 2 && cirCd.length >= 2 && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)' }}>Umfänge (Taille / Hüfte / Bauch)</div>
            <NavToCircum />
          </div>
          <div style={{ fontSize: 10, color: 'var(--text3)', marginBottom: 8, lineHeight: 1.4 }}>Mit Brust- und Taillenumfang erscheint die Proportionen-Ansicht oben.</div>
          <ResponsiveContainer width="100%" height={150}>
            <LineChart data={cirCd} margin={{ top: 4, right: 8, bottom: 0, left: -20 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }} formatter={(v, n) => [`${v} cm`, n]} />
              <Line type="monotone" dataKey="waist" stroke="#EF9F27" strokeWidth={2} dot={{ r: 3 }} name="Taille" />
              <Line type="monotone" dataKey="hip" stroke="#7F77DD" strokeWidth={2} dot={{ r: 3 }} name="Hüfte" />
              {cirCd.some(d => d.belly) && <Line type="monotone" dataKey="belly" stroke="#D4537E" strokeWidth={2} dot={{ r: 3 }} name="Bauch" />}
            </LineChart>
          </ResponsiveContainer>
        </div>
      )}

      <InsightBox insights={insights} slugs={filterActiveSlugs(['pipeline', 'koerper', 'gesundheit', 'ziele'])} onRequest={onRequest} loading={loadingSlug} />
    </div>
  )
}

/** TDEE-Linie muss in der kcal-Y-Domain liegen (sonst unsichtbar trotz Legende). */
function kcalVsWeightKcalDomain(points, tdeeRef) {
  const vals = (points || [])
    .map(d => Number(d.kcal_avg))
    .filter(v => !Number.isNaN(v))
  if (!vals.length) return ['auto', 'auto']
  let lo = Math.min(...vals)
  let hi = Math.max(...vals)
  const t = tdeeRef != null ? Number(tdeeRef) : NaN
  if (!Number.isNaN(t)) {
    lo = Math.min(lo, t)
    hi = Math.max(hi, t)
  }
  const span = hi - lo || 400
  const pad = Math.max(100, span * 0.1)
  return [Math.max(0, Math.floor(lo - pad)), Math.ceil(hi + pad)]
}

const TDEE_REF_LINE_COLOR = '#475569'

/** Legende unter dem Chart: Linien + ggf. TDEE-Referenz (gestrichelt). */
function KcalVsWeightLegend({ showTdee }) {
  const line = (color) => ({
    display: 'inline-block',
    width: 22,
    height: 3,
    background: color,
    borderRadius: 1,
    verticalAlign: 'middle',
    marginRight: 6,
  })
  return (
    <div
      className="kcal-vs-weight-legend"
      style={{
        display: 'flex',
        flexWrap: 'wrap',
        justifyContent: 'center',
        alignItems: 'center',
        gap: '12px 18px',
        marginTop: 10,
        fontSize: 10,
        color: 'var(--text2)',
        lineHeight: 1.35,
      }}
    >
      <span style={{ display: 'inline-flex', alignItems: 'center' }}>
        <span style={line('#EA580C')} />
        Ø Kalorien (7-Tage-Mittel)
      </span>
      <span style={{ display: 'inline-flex', alignItems: 'center' }}>
        <span
          style={{
            display: 'inline-block',
            width: 9,
            height: 9,
            borderRadius: '50%',
            background: '#2563EB',
            marginRight: 6,
            verticalAlign: 'middle',
          }}
        />
        Gewicht (kg)
      </span>
      {showTdee ? (
        <span style={{ display: 'inline-flex', alignItems: 'center' }}>
          <span
            style={{
              display: 'inline-block',
              width: 22,
              height: 0,
              verticalAlign: 'middle',
              marginRight: 6,
              borderTop: `2px dashed ${TDEE_REF_LINE_COLOR}`,
              opacity: 0.95,
            }}
          />
          TDEE-Referenz (geschätzt)
        </span>
      ) : null}
    </div>
  )
}

/** Kalorien (Ø 7T) vs. Gewicht — Daten aus Layer-2b-Bundle (nutrition_metrics / TDEE wie Data Layer). */
function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffDate, allTime }) {
  if (vizKcalWeight?.points?.length >= 5) {
    const tdee = vizKcalWeight.tdee_reference_kcal
    const kcalVsW = vizKcalWeight.points.map(d => ({
      ...d,
      date: fmtDate(d.date),
    }))
    const n = vizKcalWeight.common_days_count ?? kcalVsW.length
    const tdeeLabel = tdee != null && tdee > 0 ? Math.round(tdee) : null
    const kcalDomain = kcalVsWeightKcalDomain(kcalVsW, tdeeLabel)
    return (
      <div className="card" style={{ marginBottom: 12 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 6 }}>
          Kalorien (Ø 7 Tage) vs. Gewicht
        </div>
        <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
          Nur Tage mit Kalorien- und Gewichtsdaten. Linke Achse: kcal (Ø 7 Tage), rechte Achse: kg.
        </div>
        <ResponsiveContainer width="100%" height={200}>
          <LineChart data={kcalVsW} margin={{ top: 4, right: 8, bottom: 0, left: -16 }}>
            <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
            <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} interval={Math.max(0, Math.floor(kcalVsW.length / 6) - 1)} />
            <YAxis yAxisId="kcal" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={kcalDomain} />
            <YAxis yAxisId="weight" orientation="right" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
            <Tooltip
              contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }}
              formatter={(v, n) => [`${Math.round(v)} ${n === 'weight' ? 'kg' : 'kcal'}`, n === 'kcal_avg' ? 'Ø Kalorien' : 'Gewicht']}
            />
            {tdeeLabel != null && (
              <ReferenceLine
                yAxisId="kcal"
                y={tdeeLabel}
                stroke={TDEE_REF_LINE_COLOR}
                strokeDasharray="6 5"
                strokeWidth={2}
                isFront
              />
            )}
            <Line yAxisId="kcal" type="monotone" dataKey="kcal_avg" stroke="#EA580C" strokeWidth={2.5} dot={false} name="kcal_avg" />
            <Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#2563EB" strokeWidth={2.5} dot={{ r: 2, fill: '#2563EB' }} name="weight" />
          </LineChart>
        </ResponsiveContainer>
        <KcalVsWeightLegend showTdee={tdeeLabel != null} />
        <div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 8 }}>
          {tdeeLabel != null
            ? `TDEE ~${tdeeLabel} kcal · ${n} gemeinsame Tage`
            : `Keine TDEE-Referenz (Gewicht/Demografie) · ${n} gemeinsame Tage`}
        </div>
      </div>
    )
  }

  const raw = (corrRows || []).filter(d => {
    if (!d.kcal || d.weight == null) return false
    const ds = typeof d.date === 'string' ? d.date.slice(0, 10) : dayjs(d.date).format('YYYY-MM-DD')
    return allTime || ds >= cutoffDate
  })
  if (raw.length < 5) return null

  const sex = profile?.sex || 'm'
  const height = profile?.height || 178
  const latestW = raw[raw.length - 1]?.weight || 80
  const age = profile?.dob ? Math.floor((Date.now() - new Date(profile.dob)) / (365.25 * 24 * 3600 * 1000)) : 35
  const bmr = sex === 'm' ? 10 * latestW + 6.25 * height - 5 * age + 5 : 10 * latestW + 6.25 * height - 5 * age - 161
  const tdee = Math.round(bmr * 1.4)
  const kcalVsW = rollingAvg(raw.map(d => ({ ...d, date: fmtDate(d.date) })), 'kcal')
  const kcalDomainFb = kcalVsWeightKcalDomain(kcalVsW, tdee)

  return (
    <div className="card" style={{ marginBottom: 12 }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 6 }}>
        Kalorien (Ø 7 Tage) vs. Gewicht
      </div>
      <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
        Nur Tage mit Kalorien- und Gewichtsdaten. Linke Achse: kcal (Ø 7 Tage), rechte Achse: kg.
      </div>
      <ResponsiveContainer width="100%" height={200}>
        <LineChart data={kcalVsW} margin={{ top: 4, right: 8, bottom: 0, left: -16 }}>
          <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
          <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} interval={Math.max(0, Math.floor(kcalVsW.length / 6) - 1)} />
          <YAxis yAxisId="kcal" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={kcalDomainFb} />
          <YAxis yAxisId="weight" orientation="right" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
          <Tooltip
            contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }}
            formatter={(v, n) => [`${Math.round(v)} ${n === 'weight' ? 'kg' : 'kcal'}`, n === 'kcal_avg' ? 'Ø Kalorien' : 'Gewicht']}
          />
          <ReferenceLine
            yAxisId="kcal"
            y={tdee}
            stroke={TDEE_REF_LINE_COLOR}
            strokeDasharray="6 5"
            strokeWidth={2}
            isFront
          />
          <Line yAxisId="kcal" type="monotone" dataKey="kcal_avg" stroke="#EA580C" strokeWidth={2.5} dot={false} name="kcal_avg" />
          <Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#2563EB" strokeWidth={2.5} dot={{ r: 2, fill: '#2563EB' }} name="weight" />
        </LineChart>
      </ResponsiveContainer>
      <KcalVsWeightLegend showTdee />
      <div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 8 }}>
        TDEE ~{tdee} kcal (Fallback Mifflin ×1,4) · {raw.length} gemeinsame Tage
      </div>
    </div>
  )
}

// ── Nutrition Section ─────────────────────────────────────────────────────────
/** Layer 2b: Kennzahlen und Reihen nur aus GET /charts/nutrition-history-viz (nutrition_metrics). */
function NutritionSection({ profile, insights, onRequest, loadingSlug, filterActiveSlugs }) {
  const [period, setPeriod] = useState(30)
  const [groupedGoals, setGroupedGoals] = useState(null)
  const [viz, setViz] = useState(null)
  const [vizLoad, setVizLoad] = useState(true)
  const [vizErr, setVizErr] = useState(null)

  useEffect(() => {
    let cancelled = false
    api.listGoalsGrouped()
      .then(g => { if (!cancelled) setGroupedGoals(g) })
      .catch(() => { if (!cancelled) setGroupedGoals({}) })
    return () => { cancelled = true }
  }, [])

  useEffect(() => {
    let cancelled = false
    setViz(null)
    setVizLoad(true)
    setVizErr(null)
    const daysReq = period === 9999 ? 9999 : period
    api.getNutritionHistoryViz(daysReq)
      .then(v => { if (!cancelled) setViz(v) })
      .catch(e => { if (!cancelled) setVizErr(e.message || 'Laden fehlgeschlagen') })
      .finally(() => { if (!cancelled) setVizLoad(false) })
    return () => { cancelled = true }
  }, [period])

  if (vizLoad) {
    return (
      <div>
        <SectionHeader title="🍽️ Ernährung" to="/nutrition" toLabel="Import" />
        <PeriodSelector value={period} onChange={setPeriod} />
        <div className="spinner" style={{ margin: 24 }} />
      </div>
    )
  }

  if (vizErr) {
    return (
      <div>
        <SectionHeader title="🍽️ Ernährung" to="/nutrition" toLabel="Import" />
        <div className="card" style={{ color: 'var(--danger)', marginTop: 8 }}>{vizErr}</div>
      </div>
    )
  }

  if (!viz?.has_nutrition_entries) {
    return (
      <EmptySection text="Noch keine Ernährungsdaten." to="/nutrition" toLabel="FDDB importieren"/>
    )
  }

  const summary = viz.summary || {}
  const n = Math.max(0, Number(summary.data_points) || 0)
  const avgKcal = Math.round(Number(summary.kcal_avg) || 0)
  const ptLow = Math.round(Number(viz.protein_reference_line_g) || 0)
  const chartDays = viz.nutrition_charts_days || (period === 9999 ? 90 : period)
  const kpiTiles = (viz.kpi_tiles || []).map(t => ({
    ...t,
    sublabel: typeof t.sublabel === 'string' && t.sublabel.length > 36 ? `${t.sublabel.slice(0, 34)}…` : t.sublabel,
  }))
  const pieData = viz.donut_avg_pct || []
  const cdMacro = (viz.daily_macros || []).map(d => ({
    date: fmtDate(d.date),
    Protein: d.Protein,
    KH: d.KH,
    Fett: d.Fett,
    kcal: d.kcal,
  }))
  const weeklyMacro = viz.weekly_macro_chart
  const wmLoading = false
  const wmError = null
  const balDaily = viz.calorie_balance_daily || []
  const plm = viz.protein_vs_lean_mass || {}
  const plmPts = plm.points || []
  const nutHeur = viz.nutrition_correlation_heuristics || []
  const tdeeRef = viz.tdee_reference_kcal

  if (!cdMacro.length || n === 0) {
    return (
      <div>
        <SectionHeader title="🍽️ Ernährung" to="/nutrition" toLabel="Import" lastUpdated={viz.last_updated} />
        <PeriodSelector value={period} onChange={setPeriod}/>
        <EmptySection text="Keine Einträge im gewählten Zeitraum."/>
      </div>
    )
  }

  return (
    <div>
      <SectionHeader title="🍽️ Ernährung" to="/nutrition" toLabel="Import" lastUpdated={viz.last_updated}/>
      <PeriodSelector value={period} onChange={setPeriod}/>

      <p style={{ fontSize: 11, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 10 }}>
        Kennzahlen und Charts nutzen dieselbe Berechnung wie die KI-Platzhalter (Ernährungs-Data-Layer).{' '}
        <strong>Kalorien vs. Gewicht</strong> und TDEE-Referenz entsprechen Mifflin–St Jeor × PAL 1,55 bzw. kg-Fallback (32,5 kcal/kg).
        {' '}
        <strong>Kalorienbilanz</strong>, <strong>Protein vs. Magermasse</strong> und den Block{' '}
        <strong>«Kurz-Einordnung»</strong> finden Sie hier — früher im eigenen Reiter «Korrelation» (jetzt Data-Layer-Bundle).
      </p>

      <NutritionGoalsStrip grouped={groupedGoals} />

      <KpiTilesOverview tiles={kpiTiles} />

      <KcalVsWeightChart
        vizKcalWeight={viz.kcal_vs_weight}
        corrData={[]}
        profile={profile}
        cutoffDate=""
        allTime={period === 9999}
      />

      {balDaily.length > 0 && tdeeRef != null && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 6 }}>
            Kalorienbilanz (Aufnahme − TDEE ~{Math.round(tdeeRef)} kcal)
          </div>
          <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
            Tagesbilanz und 7-Tage-Mittel — gleiche TDEE-Quelle wie «Kalorien vs. Gewicht» (Data-Layer).
          </div>
          <ResponsiveContainer width="100%" height={180}>
            <LineChart
              data={balDaily.map((d) => ({ ...d, date: fmtDate(d.date) }))}
              margin={{ top: 4, right: 8, bottom: 0, left: -16 }}
            >
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} interval={Math.max(0, Math.floor(balDaily.length / 6) - 1)} />
              <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <ReferenceLine y={0} stroke="var(--text3)" strokeWidth={1.5} />
              <Tooltip
                contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }}
                formatter={(v, n) => [`${v > 0 ? '+' : ''}${v} kcal`, n === 'balance_kcal_avg' ? 'Ø 7T Bilanz' : 'Tagesbilanz']}
              />
              <Line type="monotone" dataKey="balance_kcal" stroke="#EF9F2744" strokeWidth={1} dot={false} name="balance_kcal" />
              <Line type="monotone" dataKey="balance_kcal_avg" stroke="#EF9F27" strokeWidth={2.5} dot={false} name="balance_kcal_avg" />
            </LineChart>
          </ResponsiveContainer>
        </div>
      )}

      {plmPts.length >= 3 && (
        <div className="card" style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 6 }}>
            Protein vs. Magermasse (Caliper, forward-filled)
          </div>
          <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
            Zweitachsig; gestrichelte Linie = Protein-Minimum ({plm.protein_target_low_g || ptLow || '—'} g), wenn verfügbar.
          </div>
          <ResponsiveContainer width="100%" height={180}>
            <LineChart data={plmPts.map((d) => ({ ...d, date: fmtDate(d.date), protein: d.protein_g, lean: d.lean_mass_kg }))} margin={{ top: 4, right: 8, bottom: 0, left: -16 }}>
              <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" />
              <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
              <YAxis yAxisId="prot" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              <YAxis yAxisId="lean" orientation="right" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} domain={['auto', 'auto']} />
              {plm.protein_target_low_g > 0 && (
                <ReferenceLine
                  yAxisId="prot"
                  y={plm.protein_target_low_g}
                  stroke="#1D9E75"
                  strokeDasharray="4 4"
                  strokeWidth={1.5}
                  label={{ value: `${plm.protein_target_low_g}g`, fontSize: 9, fill: '#1D9E75', position: 'right' }}
                />
              )}
              <Tooltip
                contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }}
                formatter={(v, n) => [`${v}${n === 'protein' ? 'g' : ' kg'}`, n === 'protein' ? 'Protein' : 'Mager']}
              />
              <Line yAxisId="prot" type="monotone" dataKey="protein" stroke="#1D9E75" strokeWidth={2} dot={false} name="protein" />
              <Line yAxisId="lean" type="monotone" dataKey="lean" stroke="#7F77DD" strokeWidth={2} dot={{ r: 3, fill: '#7F77DD' }} name="lean" />
            </LineChart>
          </ResponsiveContainer>
        </div>
      )}

      {nutHeur.length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 8 }}>Ernährung — Kurz-Einordnung</div>
          {nutHeur.map((item, i) => (
            <div
              key={i}
              style={{
                padding: '10px 12px',
                borderRadius: 8,
                marginBottom: 6,
                background: item.status === 'good' ? 'var(--accent-light)' : 'var(--warn-bg)',
                border: `1px solid ${item.status === 'good' ? 'var(--accent)' : 'var(--warn)'}33`,
              }}
            >
              <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                <span style={{ fontSize: 16 }}>{item.icon || '•'}</span>
                <div>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{item.title}</div>
                  <div style={{ fontSize: 12, color: 'var(--text2)', marginTop: 3, lineHeight: 1.5 }}>{item.detail}</div>
                </div>
              </div>
            </div>
          ))}
        </div>
      )}

      <div className="card" style={{ marginBottom: 12 }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 6 }}>
          Makroverteilung täglich (g) · Fokus Protein
        </div>
        <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
          Gestapelte Balken in Gramm; gestrichelte Linie = Protein-Minimum ({ptLow || '—'} g) nach 1,6 g/kg (Referenzgewicht).
        </div>
        <ResponsiveContainer width="100%" height={200}>
          <BarChart data={cdMacro} margin={{ top: 6, right: 8, bottom: 0, left: -18 }}>
            <CartesianGrid stroke="var(--border)" strokeDasharray="3 3" vertical={false} />
            <XAxis dataKey="date" tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} interval={Math.max(0, Math.floor(cdMacro.length / 6) - 1)} />
            <YAxis tick={{ fontSize: 9, fill: 'var(--text3)' }} tickLine={false} />
            {ptLow > 0 && (
              <ReferenceLine y={ptLow} stroke={MACRO_CHART.protein} strokeDasharray="5 4" strokeWidth={2} label={{ value: `${ptLow}g P`, fontSize: 9, fill: MACRO_CHART.protein, position: 'insideTopRight' }} />
            )}
            <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 11 }} formatter={(v, name) => [`${v}g`, name]} />
            <Bar dataKey="Protein" stackId="a" fill={MACRO_CHART.protein} name="Protein" />
            <Bar dataKey="Fett" stackId="a" fill={MACRO_CHART.fat} name="Fett" />
            <Bar dataKey="KH" stackId="a" fill={MACRO_CHART.carbs} name="KH" radius={[5, 5, 0, 0]} />
          </BarChart>
        </ResponsiveContainer>
        <div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 8, fontSize: 10, color: 'var(--text3)', flexWrap: 'wrap' }}>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: MACRO_CHART.protein, borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }} />Protein (unten)</span>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: MACRO_CHART.fat, borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }} />Fett (Mitte)</span>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: MACRO_CHART.carbs, borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }} />KH (oben)</span>
        </div>
      </div>

      <div className="nutrition-macro-pair">
        <div className="card nutrition-macro-pair__donut">
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 8 }}>
            Ø Makro-Quote ({n} Tage)
          </div>
          {pieData.length > 0 ? (
            <div className="nutrition-macro-pair__donut-inner">
              <div className="nutrition-macro-pair__donut-chart">
                <ResponsiveContainer width="100%" height={NUTRITION_MACRO_CHART_BLOCK_PX}>
                  <PieChart>
                    <Pie
                      data={pieData}
                      cx="50%"
                      cy="50%"
                      innerRadius="38%"
                      outerRadius="58%"
                      dataKey="value"
                      startAngle={90}
                      endAngle={-270}
                      paddingAngle={1}
                    >
                      {pieData.map((e, i) => (
                        <Cell key={i} fill={macroFillByName(e.name)} />
                      ))}
                    </Pie>
                    <Tooltip formatter={(v, name) => [`${v}%`, name]} />
                  </PieChart>
                </ResponsiveContainer>
              </div>
              <div className="nutrition-macro-pair__legend">
                {pieData.map(p => {
                  const fill = macroFillByName(p.name)
                  return (
                    <div key={p.name} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                      <div style={{ width: 10, height: 10, borderRadius: 2, background: fill, flexShrink: 0 }} />
                      <div style={{ flex: 1, fontSize: 13 }}>{p.name}</div>
                      <div style={{ fontSize: 13, fontWeight: 600, color: fill }}>{p.value}%</div>
                      <div style={{ fontSize: 11, color: 'var(--text3)' }}>
                        {p.grams != null ? `${p.grams}g` : '—'}
                      </div>
                    </div>
                  )
                })}
                <div style={{ marginTop: 8, fontSize: 11, color: 'var(--text3)', borderTop: '1px solid var(--border)', paddingTop: 8 }}>
                  Ø {avgKcal} kcal/Tag · Anteil der Makro-Kalorien am Tagesumsatz
                </div>
              </div>
            </div>
          ) : (
            <div style={{ fontSize: 12, color: 'var(--text3)' }}>Keine Makro-Mittelwerte im Zeitraum.</div>
          )}
        </div>
        <div className="card nutrition-macro-pair__weekly">
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 4 }}>
            Wöchentliche Makro-Verteilung (Backend)
          </div>
          <WeeklyMacroDistributionPanel macroWeeklyData={weeklyMacro} loading={wmLoading} error={wmError} />
        </div>
      </div>

      <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 8, marginTop: 4 }}>
        Zeitverläufe (Energie & Protein)
      </div>
      <NutritionCharts days={chartDays} showWeeklyMacroDistribution={false} hideEnergyAvailabilityCard />

      <InsightBox insights={insights} slugs={filterActiveSlugs(['ernaehrung'])} onRequest={onRequest} loading={loadingSlug}/>
    </div>
  )
}

// ── Activity Section — nur Layer-2b-Bundle (+ KI-Insights), keine parallelen Client-Charts ─
function ActivitySection({ activities, insights, onRequest, loadingSlug, filterActiveSlugs, globalQualityLevel }) {
  const [period, setPeriod] = useState(30)
  const actList = activities || []
  const hasList = actList.length > 0

  return (
    <div>
      <SectionHeader title="🏋️ Fitness" to="/activity" toLabel="Alle Einträge" lastUpdated={actList[0]?.date}/>
      <PeriodSelector value={period} onChange={setPeriod}/>
      <p style={{ fontSize: 11, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 10 }}>
        Fitness und Erholung aus den Data-Layer-Bundles (Issue 53). Zeitraum-Buttons steuern beide Bereiche gleichzeitig.
      </p>
      <FitnessDashboardOverview period={period} onPeriodChange={setPeriod} hidePeriodSelector />

      <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 8, marginTop: 20 }}>
        Erholung (Schlaf, HRV, Vitalwerte)
      </div>
      <RecoveryDashboardOverview period={period} onPeriodChange={setPeriod} hidePeriodSelector />

      {hasList && globalQualityLevel && globalQualityLevel !== 'all' && (
        <div style={{
          marginTop: 12,
          marginBottom: 12, padding:'8px 12px', borderRadius:8,
          background:'var(--surface2)', border:'1px solid var(--border)',
          fontSize:12, color:'var(--text2)', display:'flex', alignItems:'center', gap:8
        }}>
          <span>
            {globalQualityLevel === 'quality' && '✓ Filter: Hochwertig (excellent, good, acceptable)'}
            {globalQualityLevel === 'very_good' && '✓✓ Filter: Sehr gut (excellent, good)'}
            {globalQualityLevel === 'excellent' && '⭐ Filter: Exzellent (nur excellent)'}
          </span>
          <a href="/settings" style={{
            marginLeft:'auto', color:'var(--accent)', textDecoration:'none',
            fontSize:11, fontWeight:500, whiteSpace:'nowrap'
          }}>
            Hier ändern →
          </a>
        </div>
      )}

      <InsightBox
        insights={insights}
        slugs={filterActiveSlugs(['aktivitaet', 'gesundheit'])}
        onRequest={onRequest}
        loading={loadingSlug}
      />
    </div>
  )
}

function overviewSectionTone(sec) {
  const kpis = sec.kpi_short || []
  if (kpis.some((k) => k.status === 'bad')) return 'bad'
  if (kpis.some((k) => k.status === 'warn')) return 'warn'
  const interp = sec.interpretation_short || []
  if (interp.some((x) => x.status === 'bad')) return 'bad'
  if (interp.some((x) => x.status === 'warn')) return 'warn'
  const heur = sec.heuristic_short || []
  if (heur.some((h) => h.status === 'warn')) return 'warn'
  return 'good'
}

function overviewConfidenceUi(conf) {
  if (conf === 'high') return { label: 'Datenlage: gut', tone: 'good', hint: 'Ausreichend Messpunkte für sinnvolle Kurzinfos.' }
  if (conf === 'medium') return { label: 'Datenlage: mittel', tone: 'warn', hint: 'Einzelne Bereiche sind noch dünn besetzt.' }
  return { label: 'Datenlage: dünn', tone: 'bad', hint: 'Mehr Einträge verbessern die Aussagekraft.' }
}

function chartJsScatterPoints(payload) {
  const raw = payload?.data?.datasets?.[0]?.data || []
  if (!Array.isArray(raw)) return []
  return raw.map((p) => ({ x: Number(p.x), y: Number(p.y) }))
}

function driverBarFromStatus(st) {
  const s = String(st || '').toLowerCase()
  if (s.includes('hinder')) return { v: -1, fill: 'var(--danger)' }
  if (s.includes('förder') || s.includes('foerder')) return { v: 1, fill: 'var(--accent)' }
  return { v: 0.15, fill: '#6B7280' }
}

function chartJsBarRows(payload, fallbackDrivers) {
  const labels = payload?.data?.labels || []
  const values = payload?.data?.datasets?.[0]?.data || []
  const colors = payload?.data?.datasets?.[0]?.backgroundColor
  if (labels.length && values.length) {
    return labels.map((name, i) => ({
      name: name.length > 42 ? `${name.slice(0, 40)}…` : name,
      value: Number(values[i]),
      fill: Array.isArray(colors) ? colors[i] : Number(values[i]) < 0 ? '#EF4444' : '#1D9E75',
    }))
  }
  if (fallbackDrivers?.length) {
    return fallbackDrivers.map((d) => {
      const { v, fill } = driverBarFromStatus(d.status)
      return {
        name: String(d.factor || '—').length > 40 ? `${String(d.factor).slice(0, 38)}…` : String(d.factor || '—'),
        value: v,
        fill,
        subtitle: d.reason,
      }
    })
  }
  return []
}

function CorrelationScatterTile({ title, accent, payload }) {
  const meta = payload?.metadata || {}
  const pts = chartJsScatterPoints(payload)
  const hasChart = pts.length > 0 && meta.correlation != null
  const r = Number(meta.correlation)
  const strength =
    !Number.isFinite(r) ? 'bad' : Math.abs(r) >= 0.35 ? 'good' : Math.abs(r) >= 0.15 ? 'warn' : 'bad'

  return (
    <div
      className="card"
      style={{
        marginBottom: 0,
        padding: 10,
        borderLeft: `4px solid ${getStatusColor(strength)}`,
      }}
    >
      <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--text1)', marginBottom: 4 }}>{title}</div>
      <div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.35, marginBottom: 6 }}>
        r = {meta.correlation != null ? Number(meta.correlation).toFixed(3) : '—'}
        {meta.best_lag_days != null ? ` · Lag ${meta.best_lag_days} T` : ''}
        {meta.metric ? ` · ${meta.metric}` : ''}
        {meta.confidence ? ` · ${meta.confidence}` : ''}
      </div>
      {!hasChart ? (
        <div style={{ fontSize: 11, color: 'var(--text3)' }}>{meta.message || 'Keine Daten für diese Korrelation.'}</div>
      ) : (
        <ResponsiveContainer width="100%" height={118}>
          <ScatterChart margin={{ top: 2, right: 4, bottom: 2, left: -18 }}>
            <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
            <XAxis type="number" dataKey="x" domain={[0, 28]} tick={{ fontSize: 9, fill: 'var(--text3)' }} />
            <YAxis type="number" dataKey="y" domain={[-1, 1]} tick={{ fontSize: 9, fill: 'var(--text3)' }} />
            <ReferenceLine y={0} stroke="var(--text3)" strokeDasharray="4 4" />
            <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, fontSize: 10 }} />
            <Scatter name="r" data={pts} fill={accent} />
          </ScatterChart>
        </ResponsiveContainer>
      )}
      {meta.interpretation ? (
        <div style={{ fontSize: 10, color: 'var(--text2)', marginTop: 6, lineHeight: 1.4 }}>{meta.interpretation}</div>
      ) : null}
    </div>
  )
}

function DriversImpactTile({ payload, driversFallback }) {
  const meta = payload?.metadata || {}
  const rows = chartJsBarRows(payload, driversFallback)
  if (!rows.length) {
    return (
      <div className="card" style={{ padding: 12, borderLeft: '4px solid var(--border)' }}>
        <div style={{ fontSize: 11, fontWeight: 700, marginBottom: 6 }}>C4 Einflussfaktoren</div>
        <div style={{ fontSize: 11, color: 'var(--text3)' }}>{meta.message || 'Keine Treiber-Daten.'}</div>
      </div>
    )
  }
  const h = Math.min(220, Math.max(96, rows.length * 34))
  return (
    <div className="card" style={{ padding: 10, borderLeft: '4px solid var(--accent)' }}>
      <div style={{ fontSize: 11, fontWeight: 700, marginBottom: 6 }}>C4 Einflussfaktoren</div>
      <ResponsiveContainer width="100%" height={h}>
        <BarChart data={rows} layout="vertical" margin={{ left: 2, right: 6, top: 2, bottom: 2 }}>
          <XAxis type="number" domain={[-1.2, 1.2]} tick={{ fontSize: 9 }} />
          <YAxis type="category" dataKey="name" width={112} tick={{ fontSize: 9, fill: 'var(--text2)' }} />
          <Tooltip
            content={({ active, payload: pp }) => {
              if (!active || !pp?.length) return null
              const p = pp[0].payload
              return (
                <div
                  style={{
                    background: 'var(--surface)',
                    border: '1px solid var(--border)',
                    padding: '8px 10px',
                    borderRadius: 8,
                    fontSize: 11,
                    maxWidth: 280,
                  }}
                >
                  <div style={{ fontWeight: 600 }}>{p.name}</div>
                  {p.subtitle ? <div style={{ marginTop: 4, color: 'var(--text2)', lineHeight: 1.4 }}>{p.subtitle}</div> : null}
                </div>
              )
            }}
          />
          <Bar dataKey="value" radius={[0, 4, 4, 0]}>
            {rows.map((e, i) => (
              <Cell key={i} fill={e.fill} />
            ))}
          </Bar>
        </BarChart>
      </ResponsiveContainer>
    </div>
  )
}

// ── Gesamtansicht (Layer 2b: overview + Chart-Endpunkte C1–C4) ──────────────────
function HistoryOverviewSection({ insights, onRequest, loadingSlug, filterActiveSlugs }) {
  const navigate = useNavigate()
  const [period, setPeriod] = useState(30)
  const [bundle, setBundle] = useState(null)
  const [err, setErr] = useState(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    let cancelled = false
    const daysReq = period === 9999 ? 3650 : period
    setLoading(true)
    Promise.all([
      api.getHistoryOverviewViz(daysReq),
      api.getWeightEnergyCorrelationChart(14),
      api.getLbmProteinCorrelationChart(14),
      api.getLoadVitalsCorrelationChart(14),
      api.getRecoveryPerformanceChart(),
    ])
      .then(([overview, chartC1, chartC2, chartC3, chartC4]) => {
        if (!cancelled) {
          setBundle({ overview, chartC1, chartC2, chartC3, chartC4 })
          setErr(null)
        }
      })
      .catch((e) => {
        if (!cancelled) setErr(e.message || 'Laden fehlgeschlagen')
      })
      .finally(() => {
        if (!cancelled) setLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [period])

  if (loading) {
    return (
      <div>
        <SectionHeader title="📊 Gesamtansicht" />
        <PeriodSelector value={period} onChange={setPeriod} />
        <div className="spinner" style={{ margin: 24 }} />
      </div>
    )
  }
  if (err) {
    return (
      <div>
        <SectionHeader title="📊 Gesamtansicht" />
        <div className="card" style={{ color: 'var(--danger)', marginTop: 8 }}>{err}</div>
      </div>
    )
  }

  const data = bundle?.overview
  const chartC1 = bundle?.chartC1
  const chartC2 = bundle?.chartC2
  const chartC3 = bundle?.chartC3
  const chartC4 = bundle?.chartC4

  const lag = data?.lag_correlations || {}
  const c4drivers = lag.recovery_performance?.drivers || []
  const sections = data?.sections || []
  const confUi = overviewConfidenceUi(data?.confidence)

  return (
    <div>
      <SectionHeader title="📊 Gesamtansicht" />
      <PeriodSelector value={period} onChange={setPeriod} />

      <div
        style={{
          display: 'flex',
          flexWrap: 'wrap',
          alignItems: 'center',
          gap: 10,
          marginBottom: 14,
          padding: '10px 12px',
          borderRadius: 12,
          border: '1px solid var(--border)',
          background: getStatusBg(confUi.tone),
          borderLeft: `5px solid ${getStatusColor(confUi.tone)}`,
        }}
      >
        <span style={{ fontSize: 20, lineHeight: 1 }}>{confUi.tone === 'good' ? '●' : confUi.tone === 'warn' ? '◐' : '○'}</span>
        <div style={{ flex: 1, minWidth: 200 }}>
          <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text1)' }}>{confUi.label}</div>
          <div style={{ fontSize: 11, color: 'var(--text2)', marginTop: 2 }}>{confUi.hint}</div>
        </div>
      </div>

      <p style={{ fontSize: 11, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 14 }}>
        KPIs und Texte kommen aus den Layer-2b-Bundles (Körper, Ernährung, Fitness, Erholung).{' '}
        <strong>Ehem. «Korrelation»-Charts</strong> (Bilanz, Protein/Mager, Kurz-Einordnung) liegen unter{' '}
        <button type="button" className="btn btn-secondary" style={{ fontSize: 11, padding: '2px 8px' }} onClick={() => navigate('/history', { state: { tab: 'nutrition' } })}>
          Ernährung
        </button>
        . Die Kacheln C1–C4 unten nutzen dieselben Chart-Endpunkte wie die API (<code style={{ fontSize: 10 }}>/api/charts/*</code>).
      </p>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 10 }}>
        {sections.map((sec) => {
          const tone = overviewSectionTone(sec)
          const stripe = getStatusColor(tone)
          const badgeBg = getStatusBg(tone)
          return (
            <div
              key={sec.id}
              style={{
                borderRadius: 12,
                border: '1px solid var(--border)',
                borderLeft: `5px solid ${stripe}`,
                background: 'var(--surface)',
                padding: '12px 12px 12px 14px',
                boxShadow: tone === 'bad' ? '0 0 0 1px rgba(216,90,48,0.12)' : undefined,
              }}
            >
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 8, marginBottom: 8 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                  <span
                    style={{
                      display: 'inline-flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      minWidth: 30,
                      height: 30,
                      borderRadius: 10,
                      fontSize: 13,
                      fontWeight: 800,
                      color: stripe,
                      background: badgeBg,
                    }}
                  >
                    {tone === 'good' ? '✓' : tone === 'warn' ? '!' : '!!'}
                  </span>
                  <div style={{ fontSize: 15, fontWeight: 700 }}>{sec.title}</div>
                </div>
                <button
                  type="button"
                  className="btn btn-secondary"
                  style={{ fontSize: 11, padding: '4px 10px', flexShrink: 0 }}
                  onClick={() => navigate('/history', { state: { tab: sec.tab_id } })}
                >
                  Öffnen
                </button>
              </div>
              <div style={{ fontSize: 12, color: 'var(--text2)', marginBottom: 10, lineHeight: 1.45 }}>{sec.summary_line}</div>

              {(sec.kpi_short || []).length > 0 && (
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(128px, 1fr))', gap: 8, marginBottom: 8 }}>
                  {(sec.kpi_short || []).map((k, i) => (
                    <div
                      key={i}
                      style={{
                        padding: '8px 10px',
                        borderRadius: 10,
                        background: getStatusBg(k.status || 'good'),
                        border: `1px solid ${getStatusColor(k.status || 'good')}55`,
                      }}
                    >
                      <div style={{ fontSize: 9, color: 'var(--text3)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>{k.category}</div>
                      <div style={{ fontSize: 14, fontWeight: 700, color: getStatusColor(k.status || 'good'), marginTop: 2 }}>{k.value}</div>
                      {k.sublabel ? <div style={{ fontSize: 9, color: 'var(--text3)', marginTop: 2 }}>{k.sublabel}</div> : null}
                    </div>
                  ))}
                </div>
              )}

              {(sec.interpretation_short || []).map((it, i) => (
                <div key={`in-${i}`} style={{ fontSize: 11, marginBottom: 6, paddingLeft: 8, borderLeft: `3px solid ${getStatusColor(it.status || 'good')}` }}>
                  <strong style={{ color: 'var(--text1)' }}>{it.title}</strong>
                  <div style={{ color: 'var(--text2)', marginTop: 2, lineHeight: 1.4 }}>{it.detail}</div>
                </div>
              ))}
              {(sec.heuristic_short || []).map((h, i) => (
                <div key={`he-${i}`} style={{ fontSize: 11, marginTop: 6, padding: '6px 8px', borderRadius: 8, background: 'var(--surface2)' }}>
                  <strong style={{ color: h.status === 'warn' ? 'var(--warn)' : 'var(--accent)' }}>{h.title}</strong>
                  <div style={{ fontSize: 10, color: 'var(--text2)', marginTop: 2 }}>{h.detail}</div>
                </div>
              ))}
              {(sec.insights_short || []).map((ins, i) => (
                <div key={`is-${i}`} style={{ fontSize: 11, marginTop: 6, color: 'var(--text2)', lineHeight: 1.45 }}>
                  <strong>{ins.title}</strong>
                  <div>{ins.body}</div>
                </div>
              ))}
            </div>
          )
        })}
      </div>

      <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text1)', margin: '18px 0 10px' }}>Lag-Korrelationen (C1–C3)</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 10 }}>
        <CorrelationScatterTile title="C1 Energiebilanz ↔ Gewicht" accent="#1D9E75" payload={chartC1} />
        <CorrelationScatterTile title="C2 Protein ↔ Magermasse" accent="#3B82F6" payload={chartC2} />
        <CorrelationScatterTile title="C3 Last ↔ HRV/RHR" accent="#F59E0B" payload={chartC3} />
      </div>

      <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text1)', margin: '18px 0 10px' }}>Einflussfaktoren (C4)</div>
      <DriversImpactTile payload={chartC4} driversFallback={c4drivers} />

      <InsightBox insights={insights} slugs={filterActiveSlugs(['gesamt', 'ziele'])} onRequest={onRequest} loading={loadingSlug} />
    </div>
  )
}

// ── Photo Grid ────────────────────────────────────────────────────────────────
function PhotoGrid() {
  const [photos, setPhotos] = useState([])
  const [big, setBig] = useState(null)

  const load = () => api.listPhotos().then(setPhotos)

  useEffect(() => {
    load()
  }, [])

  const handleDelete = async (id) => {
    if (!confirm('Dieses Foto löschen?')) return
    try {
      await api.deletePhoto(id)
      if (big === id) setBig(null)
      await load()
    } catch (e) {
      alert(e.message || 'Löschen fehlgeschlagen')
    }
  }

  if (!photos.length) {
    return <EmptySection text="Noch keine Fotos." to="/photos" toLabel="Fotos erfassen" />
  }

  const sorted = [...photos].sort((a, b) => photoSortKey(b).localeCompare(photoSortKey(a)))
  const byMonth = new Map()
  for (const p of sorted) {
    const mk = photoMonthKey(p)
    if (!byMonth.has(mk)) byMonth.set(mk, [])
    byMonth.get(mk).push(p)
  }
  const monthKeys = [...byMonth.keys()].sort((a, b) => b.localeCompare(a))

  return (
    <>
      {big && (
        <div
          style={{
            position: 'fixed',
            inset: 0,
            background: 'rgba(0,0,0,0.9)',
            zIndex: 100,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={() => setBig(null)}
          role="presentation"
        >
          <img src={api.photoUrl(big)} style={{ maxWidth: '100%', maxHeight: '100%', borderRadius: 8 }} alt="" />
        </div>
      )}
      {monthKeys.map((mk) => (
        <div key={mk} style={{ marginBottom: 16 }}>
          <div
            style={{
              fontSize: 14,
              fontWeight: 700,
              color: 'var(--text3)',
              marginBottom: 10,
              textTransform: 'capitalize',
            }}
          >
            {dayjs(`${mk}-01`).format('MMMM YYYY')}
          </div>
          <div className="photo-grid">
            {byMonth.get(mk).map((p) => (
              <div key={p.id} style={{ position: 'relative' }}>
                <img
                  src={api.photoUrl(p.id)}
                  className="photo-thumb"
                  onClick={() => setBig(p.id)}
                  alt=""
                />
                <div
                  style={{
                    position: 'absolute',
                    bottom: 4,
                    left: 4,
                    fontSize: 9,
                    background: 'rgba(0,0,0,0.6)',
                    color: 'white',
                    padding: '1px 4px',
                    borderRadius: 3,
                  }}
                >
                  {formatPhotoCaption(p)}
                </div>
                <button
                  type="button"
                  className="btn btn-secondary"
                  style={{
                    position: 'absolute',
                    top: 4,
                    right: 4,
                    padding: 6,
                    minWidth: 0,
                    borderRadius: 8,
                  }}
                  title="Löschen"
                  onClick={(e) => {
                    e.stopPropagation()
                    handleDelete(p.id)
                  }}
                >
                  <Trash2 size={14} />
                </button>
              </div>
            ))}
          </div>
        </div>
      ))}
    </>
  )
}

// ── Main ──────────────────────────────────────────────────────────────────────
const TABS = [
  { id:'overview',    label:'📊 Gesamt'     },
  { id:'body',        label:'⚖️ Körper'     },
  { id:'nutrition',   label:'🍽️ Ernährung'  },
  { id:'activity',    label:'🏋️ Fitness'  },
  { id:'photos',      label:'📷 Fotos'       },
]

export default function History() {
  const { activeProfile } = useProfile()  // Issue #31: Get global quality filter
  const location = useLocation?.() || {}
  const [tab, setTab] = useState((location.state?.tab) || 'overview')
  const [weights,    setWeights]    = useState([])
  const [calipers,   setCalipers]   = useState([])
  const [circs,      setCircs]      = useState([])
  const [nutrition,  setNutrition]  = useState([])
  const [activities, setActivities] = useState([])
  const [insights,   setInsights]   = useState([])
  const [prompts,    setPrompts]    = useState([])
  const [profile,    setProfile]    = useState(null)
  const [loading,    setLoading]    = useState(true)
  const [loadingSlug,setLoadingSlug]= useState(null)

  const loadAll = () => Promise.all([
    api.listWeight(365), api.listCaliper(), api.listCirc(),
    api.listNutrition(90), api.listActivity(25_000),
    api.latestInsights(), api.getProfile(),
    api.listPrompts(),
  ]).then(([w,ca,ci,n,a,ins,p,pr])=>{
    setWeights(w); setCalipers(ca); setCircs(ci)
    setNutrition(n); setActivities(a)
    setInsights(Array.isArray(ins)?ins:[]); setProfile(p)
    setPrompts(Array.isArray(pr)?pr:[])
    setLoading(false)
  })

  useEffect(() => {
    loadAll()
  }, [activeProfile?.quality_filter_level])

  useEffect(() => {
    const t = location.state?.tab
    if (t === 'recovery') {
      setTab('activity')
      return
    }
    if (t === 'correlation') {
      setTab('nutrition')
      return
    }
    if (t && TABS.some(x => x.id === t)) setTab(t)
  }, [location.state?.tab])

  const requestInsight = async (slug) => {
    setLoadingSlug(slug)
    try {
      const result = await api.runInsight(slug)
      // result is already JSON, not a Response object
      const ins = await api.latestInsights()
      setInsights(Array.isArray(ins)?ins:[])
    } catch(e){
      alert('KI-Fehler: '+e.message)
    }
    finally{ setLoadingSlug(null) }
  }

  if(loading) return <div className="empty-state"><div className="spinner"/></div>

  // Filter active prompts
  const activeSlugs = prompts.filter(p=>p.active).map(p=>p.slug)
  const filterActiveSlugs = (slugs) => slugs.filter(s=>activeSlugs.includes(s))

  const sp={insights,onRequest:requestInsight,loadingSlug,filterActiveSlugs}

  return (
    <div className="history-page">
      <h1 className="page-title history-page__title">Verlauf & Auswertung</h1>
      <div className="history-page__layout">
        <nav className="history-tabs" aria-label="Verlauf-Kategorien">
          <div className="history-tabs__scroller">
            {TABS.map(t => (
              <button
                key={t.id}
                type="button"
                className={`history-tab-btn${tab === t.id ? ' history-tab-btn--active' : ''}`}
                onClick={() => setTab(t.id)}
                aria-current={tab === t.id ? 'page' : undefined}
              >
                {t.label}
              </button>
            ))}
          </div>
        </nav>
        <div className="history-content">
          {tab==='overview'    && <HistoryOverviewSection {...sp}/>}
          {tab==='body'        && <BodySection profile={profile} {...sp}/>}
          {tab==='nutrition'   && <NutritionSection profile={profile} {...sp}/>}
          {tab==='activity'    && <ActivitySection  activities={activities} globalQualityLevel={activeProfile?.quality_filter_level} {...sp}/>}
          {tab==='photos'      && <PhotoGrid/>}
        </div>
      </div>
    </div>
  )
}
