/* ===================== PAC — graphiques complémentaires ===================== */

/* 2. Météo (température extérieure) vs puissance PAC quotidienne */
function PacTempVsPower({ rows, height = 300 }) {
  const ref = useRef(null);
  const [w, setW] = useState(700);
  const [tip, setTip] = useTooltip();
  const [vis, setVis] = useState({ pac: true, temp: true, dju: true });
  const toggle = (k) => setVis(v => ({ ...v, [k]: !v[k] }));
  useEffect(() => {
    const r = new ResizeObserver(es => setW(es[0].contentRect.width));
    if (ref.current) r.observe(ref.current);
    return () => r.disconnect();
  }, []);
  const pts = rows.filter(r => r.pacReal != null && r.temp != null);
  if (!pts.length) return <div className="dim small">Pas de données température + PAC sur la période.</div>;
  const pad = { l: 46, r: 46, t: 12, b: 26 };
  const H = height, innerW = w - pad.l - pad.r, innerH = H - pad.t - pad.b;
  const n = pts.length;
  const maxPac = Math.max(0.5, ...pts.map(p => p.pacReal || 0)) * 1.12;
  const maxDju = Math.max(1, ...pts.map(p => p.dju || 0)) * 1.12;
  const temps = pts.map(p => p.temp);
  const tMin = Math.min(...temps) - 2, tMax = Math.max(...temps) + 2;
  const cx = (i) => pad.l + (n > 1 ? innerW * i / (n - 1) : innerW / 2);
  const yBase = pad.t + innerH;
  const yPac = (v) => yBase - (v / maxPac) * innerH;
  const yDju = (v) => yBase - (v / maxDju) * innerH;
  const yT = (v) => yBase - ((v - tMin) / (tMax - tMin || 1)) * innerH;
  const barW = Math.max(1, Math.min(9, innerW / n * 0.6));
  const tempPath = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${cx(i)},${yT(p.temp)}`).join(' ');
  const djuPath = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${cx(i)},${yDju(p.dju || 0)}`).join(' ');
  const step = Math.max(1, Math.ceil(n / 7));
  return (
    <div ref={ref} style={{ width: '100%', position: 'relative' }}>
      <svg viewBox={`0 0 ${w} ${H}`} width="100%" height={H}>
        {[0, 0.5, 1].map((f, i) => (
          <g key={i}>
            <line x1={pad.l} x2={w - pad.r} y1={yBase - f * innerH} y2={yBase - f * innerH} stroke="var(--line-soft)" strokeDasharray="2 4" />
            <text x={pad.l - 6} y={yBase - f * innerH + 3} fontSize="9" fill="var(--ink-mute)" textAnchor="end" fontFamily="Geist Mono">{fmt.k(maxPac * f, 1)}</text>
            <text x={w - pad.r + 6} y={yBase - f * innerH + 3} fontSize="9" fill="#e8a33d" textAnchor="start" fontFamily="Geist Mono">{Math.round(tMin + (tMax - tMin) * f)}°</text>
          </g>
        ))}
        {vis.pac && pts.map((p, i) => (
          <rect key={i} x={cx(i) - barW / 2} y={yPac(p.pacReal || 0)} width={barW} height={Math.max(0, yBase - yPac(p.pacReal || 0))}
                fill="var(--accent)" opacity="0.55" rx="1"
                onMouseEnter={(e) => setTip({ x: e.clientX, y: e.clientY, content: <>
                  <div className="tt-date">{p.date}</div>
                  <div className="tt-row"><span>PAC</span><span>{fmt.k(p.pacReal, 2)} kWh</span></div>
                  <div className="tt-row"><span>Temp. ext.</span><span>{fmt.k(p.temp, 1)} °C</span></div>
                  <div className="tt-row"><span>DJU</span><span>{fmt.k(p.dju || 0, 1)}</span></div>
                  {p.dju > 2 && <div className="tt-row"><span>Ratio</span><span>{fmt.k((p.pacChaufNet || 0) / p.dju, 3)} kWh/DJU</span></div>}
                </> })}
                onMouseLeave={() => setTip(null)} />
        ))}
        {vis.dju && <path d={djuPath} fill="none" stroke="var(--ink-mute)" strokeWidth="1.2" strokeDasharray="3 3" />}
        {vis.temp && <path d={tempPath} fill="none" stroke="#e8a33d" strokeWidth="2" />}
        {pts.map((p, i) => i % step === 0 && (
          <text key={'x' + i} x={cx(i)} y={H - 7} fontSize="9" fill="var(--ink-mute)" textAnchor="middle" fontFamily="Geist Mono">{p.date.slice(5)}</text>
        ))}
      </svg>
      <div className="legend" style={{ marginTop: 4 }}>
        <div className="legend-item" style={{ cursor: 'pointer', opacity: vis.pac ? 1 : .35 }} onClick={() => toggle('pac')}><span className="legend-swatch" style={{ background: 'var(--accent)', opacity: .55 }} />Conso PAC (kWh)</div>
        <div className="legend-item" style={{ cursor: 'pointer', opacity: vis.temp ? 1 : .35 }} onClick={() => toggle('temp')}><span className="legend-swatch" style={{ background: '#e8a33d' }} />Temp. extérieure (°C)</div>
        <div className="legend-item" style={{ cursor: 'pointer', opacity: vis.dju ? 1 : .35 }} onClick={() => toggle('dju')}><span className="legend-swatch" style={{ background: 'var(--ink-mute)' }} />DJU</div>
      </div>
      {tip}
    </div>
  );
}

/* 4. Talon ECS isolé — moyenne quotidienne par mois, référence = mois sans chauffe */
function PacEcsBaseline({ rows, height = 240 }) {
  const ref = useRef(null);
  const [w, setW] = useState(600);
  const [tip, setTip] = useTooltip();
  useEffect(() => {
    const r = new ResizeObserver(es => setW(es[0].contentRect.width));
    if (ref.current) r.observe(ref.current);
    return () => r.disconnect();
  }, []);
  const byMonth = useMemo(() => {
    const m = new Map();
    for (const r of rows) {
      if (r.pacEcs == null) continue;
      const k = r.date.slice(0, 7);
      if (!m.has(k)) m.set(k, { key: k, ecs: 0, days: 0, popDays: 0, dju: 0, pacReal: 0 });
      const b = m.get(k);
      b.ecs += r.pacEcs || 0; b.days++; b.dju += r.dju || 0; b.pacReal += r.pacReal || 0;
      if ((r.pacEcs || 0) > 0) b.popDays++;
    }
    return [...m.values()].map(b => ({ ...b, perDay: b.ecs / Math.max(1, b.days), djuPerDay: b.dju / Math.max(1, b.days) }))
      .sort((a, b) => a.key.localeCompare(b.key));
  }, [rows]);
  const populatedDays = rows.filter(r => (r.pacEcs || 0) > 0).length;
  const incoherent = rows.filter(r => (r.pacEcs || 0) > (r.pacReal || 0) * 1.05 + 0.2 && (r.pacEcs || 0) > 0).length;
  if (populatedDays > 0 && incoherent / populatedDays >= 0.2) return (
    <div className="dim small" style={{padding:'18px 0'}}>
      Colonne ECS incohérente : sur {incoherent} des {populatedDays} jours renseignés, <span className="mono">Conso_PAC_ECS_Pics</span> dépasse la conso PAC totale du même jour — un sous-compteur ne peut pas excéder son compteur parent. Vérifiez l'unité (Wh vs kWh) ou le caractère journalier de la colonne.
    </div>
  );
  if (populatedDays < 10) return (
    <div className="dim small" style={{padding:'18px 0'}}>
      Trop peu de jours renseignés en ECS ({populatedDays}) pour établir une moyenne mensuelle fiable. Étendez les formules de la colonne <span className="mono">Conso_PAC_ECS_Pics</span> à l'historique complet.
    </div>
  );
  if (!byMonth.length) return <div className="dim small">Pas de données ECS sur la période.</div>;
  const summer = byMonth.filter(b => b.djuPerDay < 1 && b.popDays >= 10);
  const baseline = summer.length >= 2 ? summer.reduce((a, b) => a + b.perDay, 0) / summer.length : null;
  const pad = { l: 42, r: 14, t: 12, b: 26 };
  const H = height, innerW = w - pad.l - pad.r, innerH = H - pad.t - pad.b;
  const n = byMonth.length;
  const maxV = Math.max(0.5, ...byMonth.map(b => b.perDay)) * 1.15;
  const yBase = pad.t + innerH;
  const y = (v) => yBase - (v / maxV) * innerH;
  const slot = innerW / n;
  const barW = Math.min(28, slot * 0.6);
  const step = Math.max(1, Math.ceil(n / 8));
  return (
    <div ref={ref} style={{ width: '100%', position: 'relative' }}>
      <svg viewBox={`0 0 ${w} ${H}`} width="100%" height={H}>
        {[0, 0.5, 1].map((f, i) => (
          <g key={i}>
            <line x1={pad.l} x2={w - pad.r} y1={yBase - f * innerH} y2={yBase - f * innerH} stroke="var(--line-soft)" strokeDasharray="2 4" />
            <text x={pad.l - 6} y={yBase - f * innerH + 3} fontSize="9" fill="var(--ink-mute)" textAnchor="end" fontFamily="Geist Mono">{fmt.k(maxV * f, 1)}</text>
          </g>
        ))}
        {byMonth.map((b, i) => {
          const isSummer = b.djuPerDay < 1;
          const x = pad.l + slot * i + (slot - barW) / 2;
          return (
            <rect key={b.key} x={x} y={y(b.perDay)} width={barW} height={Math.max(1, yBase - y(b.perDay))} rx="2"
                  fill={isSummer ? '#7fbf7f' : 'var(--accent)'} opacity={isSummer ? 0.9 : 0.5}
                  onMouseEnter={(e) => setTip({ x: e.clientX, y: e.clientY, content: <>
                    <div className="tt-date">{b.key}</div>
                    <div className="tt-row"><span>ECS / jour</span><span>{fmt.k(b.perDay, 2)} kWh</span></div>
                    <div className="tt-row"><span>DJU / jour</span><span>{fmt.k(b.djuPerDay, 1)}</span></div>
                    {baseline && <div className="tt-row"><span>vs talon été</span><span>{b.perDay > baseline ? '+' : ''}{fmt.k((b.perDay - baseline) / baseline * 100, 0)}%</span></div>}
                    <div className="tt-row" style={{ opacity: .7 }}><span>{isSummer ? 'mois hors chauffe' : 'mois de chauffe'}</span><span>{b.days} j</span></div>
                  </> })}
                  onMouseLeave={() => setTip(null)} />
          );
        })}
        {baseline != null && <>
          <line x1={pad.l} x2={w - pad.r} y1={y(baseline)} y2={y(baseline)} stroke="#7fbf7f" strokeWidth="1.5" strokeDasharray="5 3" />
          <text x={w - pad.r} y={y(baseline) - 5} fontSize="9" fill="#7fbf7f" textAnchor="end" fontFamily="Geist Mono">talon {fmt.k(baseline, 2)} kWh/j</text>
        </>}        {byMonth.map((b, i) => i % step === 0 && (
          <text key={'x' + i} x={pad.l + slot * i + slot / 2} y={H - 7} fontSize="9" fill="var(--ink-mute)" textAnchor="middle" fontFamily="Geist Mono">{b.key.slice(2)}</text>
        ))}
      </svg>
      <div className="legend" style={{ marginTop: 4 }}>
        <div className="legend-item"><span className="legend-swatch" style={{ background: '#7fbf7f' }} />Mois hors chauffe (ECS pure)</div>
        <div className="legend-item"><span className="legend-swatch" style={{ background: 'var(--accent)', opacity: .5 }} />Mois de chauffe</div>
      </div>
      {tip}
    </div>
  );
}

/* 5. Part de la PAC en heures creuses — estimation par régression HC ~ conso PAC */
function PacHcHpEstimate({ rows }) {
  const [use, setUse] = useState({ ecs: true, chauf: true, clim: false });
  const toggle = (k) => setUse(u => ({ ...u, [k]: !u[k] }));
  const anySelected = use.ecs || use.chauf || use.clim;
  const est = useMemo(() => {
    if (!anySelected) return null;
    const load = (r) => (use.ecs ? (r.pacEcs || 0) : 0) + (use.chauf ? (r.pacChaufNet || 0) : 0) + (use.clim ? (r.pacClimNet || 0) : 0);
    const pts = rows.filter(r => load(r) > 0.2 && r.hc != null && r.hp != null);
    if (pts.length < 20) return null;
    const n = pts.length;
    const mx = pts.reduce((a, r) => a + load(r), 0) / n;
    const my = pts.reduce((a, r) => a + r.hc, 0) / n;
    let num = 0, den = 0;
    for (const r of pts) { num += (load(r) - mx) * (r.hc - my); den += (load(r) - mx) ** 2; }
    if (den <= 0) return null;
    const b = num / den;
    const sy = Math.sqrt(pts.reduce((a, r) => a + (r.hc - my) ** 2, 0) / n);
    const sx = Math.sqrt(den / n);
    const rho = sx > 0 && sy > 0 ? (num / n) / (sx * sy) : 0;
    const share = Math.max(0, Math.min(1, b));
    const total = pts.reduce((a, r) => a + load(r), 0);
    return { share, rho, n, total, hcKwh: total * share, hpKwh: total * (1 - share) };
  }, [rows, use.ecs, use.chauf, use.clim, anySelected]);
  const boxes = (
    <div className="legend" style={{ marginBottom: 12 }}>
      {[['ecs', 'ECS'], ['chauf', 'Chauffage'], ['clim', 'Climatisation']].map(([k, lbl]) => (
        <label key={k} className="legend-item" style={{ cursor: 'pointer', opacity: use[k] ? 1 : .45 }}>
          <input type="checkbox" checked={use[k]} onChange={() => toggle(k)} style={{ marginRight: 4 }} />{lbl}
        </label>
      ))}
    </div>
  );
  if (!anySelected) return <div>{boxes}<div className="dim small">Cochez au moins un poste pour estimer sa répartition HC/HP.</div></div>;
  if (!est) return <div>{boxes}<div className="dim small">Pas assez de jours mesurés sur les postes cochés pour estimer la répartition HC/HP.</div></div>;
  const pct = Math.round(est.share * 100);
  const reliable = Math.abs(est.rho) > 0.35;
  const label = [use.ecs && 'ECS', use.chauf && 'chauffage', use.clim && 'clim'].filter(Boolean).join(' + ');
  return (
    <div>
      {boxes}
      <div className="row-space" style={{ alignItems: 'flex-end', marginBottom: 10 }}>
        <div>
          <div className="mono" style={{ fontSize: 30, lineHeight: 1 }}>{pct}<span className="unit">% en HC</span></div>
          <div className="dim small" style={{ marginTop: 4 }}>{label} · {est.n} jours · corrélation {fmt.k(est.rho, 2)}</div>
        </div>
        <div className="mono dim small" style={{ textAlign: 'right' }}>
          {fmt.k(est.hcKwh, 0)} kWh HC<br />{fmt.k(est.hpKwh, 0)} kWh HP
        </div>
      </div>
      <div style={{ height: 14, borderRadius: 7, overflow: 'hidden', display: 'flex', background: 'var(--line-soft)' }}>
        <div style={{ width: `${pct}%`, background: '#7fbf7f' }} />
        <div style={{ width: `${100 - pct}%`, background: 'var(--grid-red)' }} />
      </div>
      <div className="legend" style={{ marginTop: 6 }}>
        <div className="legend-item"><span className="legend-swatch" style={{ background: '#7fbf7f' }} />Heures creuses</div>
        <div className="legend-item"><span className="legend-swatch" style={{ background: 'var(--grid-red)' }} />Heures pleines</div>
      </div>
      <div className="dim small" style={{ marginTop: 10 }}>
        {reliable
          ? (pct >= 60
            ? `Le poste ${label} tourne majoritairement en heures creuses — peu de gain à espérer d'un décalage supplémentaire.`
            : `Une part notable du poste ${label} tombe en heures pleines. Décaler ce poste vers la plage creuse rapporterait environ ${fmt.eur(est.hpKwh * 0.5 * 0.0487, 0)} en déplaçant la moitié.`)
          : `Corrélation faible sur ${label} : ce poste ne se distingue pas assez du reste du foyer pour une estimation fiable.`}
      </div>
      <div className="dim small" style={{ marginTop: 6, opacity: .7 }}>
        Méthode : régression linéaire de la conso HC quotidienne du foyer sur la conso des postes cochés. La pente estime la fraction consommée en plage creuse — inférence statistique, pas une mesure directe.
      </div>
    </div>
  );
}

/* 6. Ratio kWh/DJU en fonction de la température extérieure — sépare l'inefficacité
   normale par grand froid (chute du COP) d'une vraie dérive de l'installation.
   Une courbe médiane par tranche de 2 °C sert de référence propre au logement. */
function PacRatioVsTemp({ rows, height = 300 }) {
  const ref = useRef(null);
  const [w, setW] = useState(700);
  const [tip, setTip] = useTooltip();
  const [vis, setVis] = useState({ points: true, median: true, band: true });
  const toggle = (k) => setVis(v => ({ ...v, [k]: !v[k] }));
  useEffect(() => {
    const r = new ResizeObserver(es => setW(es[0].contentRect.width));
    if (ref.current) r.observe(ref.current);
    return () => r.disconnect();
  }, []);

  const seasonOf = (d) => {
    const y = +d.slice(0, 4), m = +d.slice(5, 7);
    return m >= 8 ? `${y}-${String((y + 1) % 100).padStart(2, '0')}` : `${y - 1}-${String(y % 100).padStart(2, '0')}`;
  };
  const pts = useMemo(() => rows
    .filter(r => (r.dju || 0) >= 3 && (r.pacChaufNet || 0) > 0 && r.temp != null)
    .map(r => ({ t: r.temp, ratio: r.pacChaufNet / r.dju, date: r.date, season: seasonOf(r.date), dju: r.dju })),
    [rows]);

  if (pts.length < 15) return (
    <div className="dim small" style={{padding:'18px 0'}}>
      Pas assez de jours de chauffe avec température et chauffage net renseignés ({pts.length}) pour dégager une tendance.
    </div>
  );

  const seasons = [...new Set(pts.map(p => p.season))].sort();
  const palette = ['#7ab0d9', '#f4b942', 'var(--grid-red)', '#7fbf7f', '#b98fd9'];
  const colorOf = (s) => palette[seasons.indexOf(s) % palette.length];

  // Médiane + écart-type par tranche de 2 °C
  const bins = new Map();
  for (const p of pts) {
    const k = Math.floor(p.t / 2) * 2;
    if (!bins.has(k)) bins.set(k, []);
    bins.get(k).push(p.ratio);
  }
  const binStats = [...bins.entries()]
    .filter(([, v]) => v.length >= 3)
    .map(([k, v]) => {
      const sorted = [...v].sort((a, b) => a - b);
      const med = sorted[Math.floor(sorted.length / 2)];
      const mean = v.reduce((a, x) => a + x, 0) / v.length;
      const sd = Math.sqrt(v.reduce((a, x) => a + (x - mean) ** 2, 0) / v.length);
      return { t: k + 1, med, sd, n: v.length };
    })
    .sort((a, b) => a.t - b.t);

  const pad = { l: 46, r: 16, t: 14, b: 34 };
  const H = height;
  const innerW = w - pad.l - pad.r, innerH = H - pad.t - pad.b;
  const tMin = Math.min(...pts.map(p => p.t)), tMax = Math.max(...pts.map(p => p.t));
  const rMax = Math.max(...pts.map(p => p.ratio)) * 1.1 || 1;
  const x = (t) => pad.l + ((t - tMin) / Math.max(0.1, tMax - tMin)) * innerW;
  const y = (v) => pad.t + innerH - (v / rMax) * innerH;

  const medPath = binStats.map((b, i) => `${i ? 'L' : 'M'}${x(b.t)},${y(b.med)}`).join(' ');
  const bandPath = binStats.length > 1
    ? binStats.map((b, i) => `${i ? 'L' : 'M'}${x(b.t)},${y(b.med + b.sd)}`).join(' ')
      + ' ' + [...binStats].reverse().map(b => `L${x(b.t)},${y(Math.max(0, b.med - b.sd))}`).join(' ') + ' Z'
    : null;

  const yTicks = [0, rMax / 2, rMax];
  const xTicks = [];
  for (let t = Math.ceil(tMin / 5) * 5; t <= tMax; t += 5) xTicks.push(t);

  // Jours nettement au-dessus de la médiane de leur tranche → suspects
  const outliers = pts.filter(p => {
    const b = binStats.find(b => Math.abs(b.t - (Math.floor(p.t / 2) * 2 + 1)) < 0.01);
    return b && b.sd > 0 && p.ratio > b.med + 2 * b.sd;
  });

  return (
    <div ref={ref} style={{width:'100%', position:'relative'}}>
      <svg viewBox={`0 0 ${w} ${H}`} width="100%" height={H}>
        {yTicks.map((t, i) => (
          <g key={i}>
            <line x1={pad.l} x2={w - pad.r} y1={y(t)} y2={y(t)} stroke="var(--line-soft)" strokeDasharray="2 4" />
            <text x={pad.l - 6} y={y(t) + 3} fontSize="9" fill="var(--ink-mute)" textAnchor="end" fontFamily="Geist Mono">{fmt.k(t, 2)}</text>
          </g>
        ))}
        {xTicks.map((t, i) => (
          <text key={'x'+i} x={x(t)} y={H - 14} fontSize="9" fill="var(--ink-mute)" textAnchor="middle" fontFamily="Geist Mono">{t}°</text>
        ))}
        <text x={pad.l + innerW / 2} y={H - 2} fontSize="9" fill="var(--ink-mute)" textAnchor="middle">Température extérieure moyenne du jour</text>
        {vis.band && bandPath && <path d={bandPath} fill="var(--green)" fillOpacity=".12" />}
        {vis.points && pts.map((p, i) => (
          <circle key={i} cx={x(p.t)} cy={y(p.ratio)} r="3" fill={colorOf(p.season)} fillOpacity=".55"
                  onMouseEnter={(e) => setTip({ x: e.clientX, y: e.clientY, content: <>
                    <div className="tt-date">{p.date}</div>
                    <div className="tt-row"><span>Température</span><span>{fmt.k(p.t, 1)} °C</span></div>
                    <div className="tt-row"><span>Ratio</span><span>{fmt.k(p.ratio, 2)} kWh/DJU</span></div>
                    <div className="tt-row"><span>DJU</span><span>{fmt.k(p.dju, 1)}</span></div>
                  </>})}
                  onMouseLeave={() => setTip(null)} />
        ))}
        {vis.median && medPath && <path d={medPath} fill="none" stroke="var(--green)" strokeWidth="2.5" />}
        {outliers.map((p, i) => (
          <circle key={'o'+i} cx={x(p.t)} cy={y(p.ratio)} r="5.5" fill="none" stroke="var(--grid-red)" strokeWidth="1.5" />
        ))}
      </svg>
      <div className="legend" style={{marginTop:4}}>
        <div className="legend-item" style={{cursor:'pointer', opacity:vis.points?1:.35}} onClick={() => toggle('points')}>
          <span className="legend-swatch" style={{background:'#7ab0d9'}}></span>Jours de chauffe
        </div>
        <div className="legend-item" style={{cursor:'pointer', opacity:vis.median?1:.35}} onClick={() => toggle('median')}>
          <span className="legend-swatch" style={{background:'var(--green)'}}></span>Médiane par 2 °C
        </div>
        <div className="legend-item" style={{cursor:'pointer', opacity:vis.band?1:.35}} onClick={() => toggle('band')}>
          <span className="legend-swatch" style={{background:'var(--green)', opacity:.35}}></span>±1 écart-type
        </div>
        {outliers.length > 0 && (
          <div className="legend-item"><span className="legend-swatch" style={{background:'transparent', border:'1.5px solid var(--grid-red)'}}></span>{outliers.length} jour(s) anormal(aux)</div>
        )}
      </div>
      {tip}
    </div>
  );
}

Object.assign(window, { PacTempVsPower, PacEcsBaseline, PacHcHpEstimate, PacRatioVsTemp });

