/* global React, WeatherData, fmtNum */
const { useState, useRef, useMemo } = React;

// Filter chips rendered separately so Dashboard can lift them into the widget
// header (next to the title) — keeping the chart body free for the SVG itself.
window.ChartFilters = function ChartFilters({ config, onConfigChange }) {
  const toggle = (key) => onConfigChange({ ...config, [key]: !config[key] });
  return (
    <div className="chart-toolbar chart-toolbar-header">
      <button className={`chip-toggle ${config.temp?'on':'off'}`} onClick={() => toggle('temp')}>
        <span className="swatch" style={{background:'var(--temp-warm)'}}></span>Teplota
      </button>
      <button className={`chip-toggle ${config.precip?'on':'off'}`} onClick={() => toggle('precip')}>
        <span className="swatch" style={{background:'var(--rain)'}}></span>Srážky
      </button>
      <button className={`chip-toggle ${config.wind?'on':'off'}`} onClick={() => toggle('wind')}>
        <span className="swatch" style={{background:'#b18cff'}}></span>Vítr
      </button>
      <button className={`chip-toggle ${config.humidity?'on':'off'}`} onClick={() => toggle('humidity')}>
        <span className="swatch" style={{background:'#5de8b5'}}></span>Vlhkost
      </button>
      <button className={`chip-toggle ${config.icons?'on':'off'}`} onClick={() => toggle('icons')}>
        <i className="bi bi-images"></i>Ikony
      </button>
    </div>
  );
};

// =========================================================
// Graf předpovědi — lanes on one shared time axis, ONE scale per lane
// (temperature °C / precipitation mm / wind m/s / humidity %) instead of
// four quantities squeezed onto the °C axis. One crosshair runs through all
// lanes with a tooltip of every value at that hour. Colours = the station
// page's quantity colours; temperature is coloured by the temperature scale.
// Night shading, midnight lines with day names, optional weather icons.
// Drawn in real pixels (useBox).
// =========================================================
const LANE_WEIGHT = { temp: 0.5, precip: 0.18, wind: 0.2, humidity: 0.12, pressure: 0.14 };

window.ChartWidget = function ChartWidget({ hourly, config, onConfigChange, widgetConfig = {} }) {
  const plotRef = useRef(null);
  const box = window.useBox(plotRef);
  const N = Math.min(config.hours, hourly ? hourly.length : 0);
  const data = (hourly || []).slice(0, N);
  const lanes = ['temp', 'precip', 'wind', 'humidity', 'pressure'].filter(k => config[k]);
  const showNight = widgetConfig.showNightShade !== false;
  const showGrid = widgetConfig.showGrid !== false;
  const showDays = widgetConfig.showDayLabels !== false;
  const wConv = (v) => (window.Units ? window.Units.wind(v) : v);
  const wUnit = window.Units ? window.Units.windLabel() : 'm/s';

  let svg = null;
  if (box && box.w > 80 && box.h > 80 && data.length > 1 && lanes.length) {
    const W = box.w, H = box.h, dark = window.isDarkTheme();
    const fs = window.fontScale();
    const padL = Math.round(40 * fs), padR = 12, padB = Math.round(22 * fs);
    const padT = Math.round((showDays ? 20 : 6) * fs) + (config.icons ? 28 : 0);
    // Every lane: a 16 px header (name left, summary right) above its plot,
    // so labels never sit on the data or on a neighbouring lane.
    const HEAD = Math.round(16 * fs), laneGap = 8;
    const avail = H - padT - padB - (HEAD + laneGap) * lanes.length + laneGap;
    const wsum = lanes.reduce((s, k) => s + LANE_WEIGHT[k], 0);
    // share the height by weight, at least MIN px per lane — what the small
    // lanes need is taken from the big ones, so the lanes never run past the
    // time axis (5 lanes used to push „Tlak“ over the hour labels)
    const MIN = 26;
    let hs = lanes.map(k => (avail * LANE_WEIGHT[k]) / wsum);
    const short = hs.reduce((s, h) => s + Math.max(0, MIN - h), 0);
    if (short > 0) {
      const spare = hs.reduce((s, h) => s + Math.max(0, h - MIN), 0);
      const f = spare > 0 ? Math.max(0, (spare - short) / spare) : 0;
      hs = hs.map(h => (h <= MIN ? MIN : MIN + (h - MIN) * f));
    }
    let cursor = padT;
    const L = {};
    lanes.forEach((k, j) => { const h = Math.floor(hs[j]); L[k] = { head: cursor, top: cursor + HEAD, h }; cursor += HEAD + h + laneGap; });
    const t0 = data[0].time.getTime(), t1 = data[data.length - 1].time.getTime();
    const x = (t) => padL + ((t - t0) / Math.max(1, t1 - t0)) * (W - padL - padR);
    const step = (W - padL - padR) / Math.max(1, data.length - 1);
    const scale = (k, lo, hi) => (v) => L[k].top + L[k].h - ((v - lo) / Math.max(1e-6, hi - lo)) * L[k].h;
    const parts = [];
    // a band [cx - w/2, cx + w/2] clipped to the plot (few hours = wide bars)
    const span = (cx, w) => { const a = Math.max(padL, cx - w / 2), b = Math.min(W - padR, cx + w / 2); return [a, Math.max(0, b - a)]; };

    // night shading + midnight lines (all lanes)
    if (showNight) {
      data.forEach((d, i) => {
        const hr = d.time.getHours();
        if (hr >= 6 && hr < 20) return;
        const [nx, nw] = span(x(d.time.getTime()), step);
        parts.push(<rect key={`n${i}`} x={nx} y={padT} width={nw} height={H - padT - padB} className="fc-night" />);
      });
    }
    data.forEach((d, i) => {
      if (i === 0 || d.time.getHours() !== 0) return;
      const tx = x(d.time.getTime());
      parts.push(<line key={`m${i}`} x1={tx} x2={tx} y1={padT - (showDays ? 16 : 0)} y2={H - padB} className="fc-midnight" />);
    });
    if (showDays) {
      const byDay = new Map();
      data.forEach((d, i) => { const k = d.time.toDateString(); if (!byDay.has(k)) byDay.set(k, []); byDay.get(k).push(i); });
      byDay.forEach((idx, k) => {
        if (idx.length < 4) return;
        const mid = idx.find(i => data[i].time.getHours() === 12) ?? idx[Math.floor(idx.length / 2)];
        const d = data[mid].time;
        parts.push(<text key={`d${k}`} x={x(d.getTime())} y={Math.round(13 * fs)} textAnchor="middle" className="fc-day">{WeatherData.WD_LONG[d.getDay()]} {d.getDate()}.</text>);
      });
    }
    if (config.icons) {
      const every = Math.max(1, Math.ceil(30 / step));
      data.forEach((d, i) => {
        if (i % every) return;
        parts.push(<image key={`i${i}`} href={WeatherData.iconUrl(d.icon)} x={x(d.time.getTime()) - 11} y={padT - 27} width="22" height="22"><title>{d.cond}</title></image>);
      });
    }

    const laneFrame = (k, label, ticks, y, fmt, summary) => {
      parts.push(<text key={`${k}-lab`} x={padL} y={L[k].head + Math.round(11 * fs)} className="fc-lane">{label}</text>);
      if (summary) parts.push(<text key={`${k}-sum`} x={W - padR} y={L[k].head + Math.round(11 * fs)} textAnchor="end" className="fc-sum">{summary}</text>);
      if (showGrid) ticks.forEach(v => parts.push(<line key={`${k}-g${v}`} x1={padL} x2={W - padR} y1={y(v)} y2={y(v)} className="fc-grid" />));
      ticks.forEach(v => parts.push(<text key={`${k}-t${v}`} x={padL - 6} y={y(v) + 4} textAnchor="end" className="fc-axis">{fmt(v)}</text>));
    };

    if (L.temp) {
      const vals = data.map(d => d.temp);
      const nt = window.niceTicks(Math.min(...vals), Math.max(...vals), Math.max(3, Math.min(6, Math.floor(L.temp.h / 26))));
      const y = scale('temp', nt.lo, nt.hi);
      laneFrame('temp', 'TEPLOTA', nt.ticks, y, v => fmtNum(v, nt.step % 1 ? 1 : 0) + '°',
        `${fmtNum(Math.min(...vals), 0)} až ${fmtNum(Math.max(...vals), 0)} °C`);
      const pts = data.map(d => [x(d.time.getTime()), y(d.temp)]);
      const line = window.smoothPath(pts);
      parts.push(<defs key="tg"><window.TempGradient id="fcT" y={y} lo={nt.lo} hi={nt.hi} dark={dark} /><window.TempGradient id="fcTf" y={y} lo={nt.lo} hi={nt.hi} dark={dark} opacity={0.18} /></defs>);
      parts.push(<path key="ta" d={`${line} L ${pts[pts.length - 1][0]} ${L.temp.top + L.temp.h} L ${pts[0][0]} ${L.temp.top + L.temp.h} Z`} fill="url(#fcTf)" />);
      parts.push(<path key="tl" d={line} fill="none" stroke="url(#fcT)" strokeWidth="2.2" strokeLinejoin="round" />);
      // label the daily extremes right at their points
      const byDay = new Map();
      data.forEach((d, i) => { const k = d.time.toDateString(); const e = byDay.get(k) || { mx: i, mn: i }; if (d.temp > data[e.mx].temp) e.mx = i; if (d.temp < data[e.mn].temp) e.mn = i; byDay.set(k, e); });
      byDay.forEach(e => {
        [['mx', -9], ['mn', 15]].forEach(([key, dy]) => {
          const i = e[key];
          if (i === 0 || i === data.length - 1) return;
          // keep the label inside the lane: a max at the top goes under its point
          let ly = pts[i][1] + dy;
          if (ly < L.temp.top + 10) ly = pts[i][1] + 15;
          if (ly > L.temp.top + L.temp.h - 2) ly = pts[i][1] - 9;
          parts.push(<text key={`e${key}${i}`} x={pts[i][0]} y={ly} textAnchor="middle" className="fc-ext">{fmtNum(data[i].temp, 0)}°</text>);
        });
      });
    }
    if (L.precip) {
      const pm = Math.max(1, ...data.map(d => d.precip || 0));
      const nt = window.niceTicks(0, pm, 2);
      const y = scale('precip', 0, nt.hi);
      const total = data.reduce((s, d) => s + (d.precip > 0 ? d.precip : 0), 0);
      laneFrame('precip', 'SRÁŽKY mm/h', [nt.hi], y, v => fmtNum(v, nt.hi < 3 ? 1 : 0),
        total > 0.05 ? `celkem ${fmtNum(total, 1)} mm` : 'bez srážek');
      parts.push(<line key="pb" x1={padL} x2={W - padR} y1={L.precip.top + L.precip.h} y2={L.precip.top + L.precip.h} className="fc-base" />);
      const bw = Math.max(2, step - 2);
      data.forEach((d, i) => {
        if (!(d.precip > 0.05)) return;
        const tx = x(d.time.getTime());
        const [bx, bw2] = span(tx, bw);
        parts.push(<rect key={`p${i}`} x={bx} y={y(d.precip)} width={bw2} height={L.precip.top + L.precip.h - y(d.precip)} rx="2" className="fc-rain" />);
      });
    }
    if (L.wind) {
      const gm = Math.max(2, ...data.map(d => wConv(Math.max(d.gust || 0, d.wind || 0))));
      const nt = window.niceTicks(0, gm, 3);
      const y = scale('wind', 0, nt.hi);
      const gMax = Math.max(...data.map(d => wConv(d.gust || 0)));
      laneFrame('wind', `VÍTR ${wUnit} · nárazy`, [nt.hi], y, v => fmtNum(v, 0),
        `nárazy až ${fmtNum(gMax, 0)} ${wUnit}`);
      const wp = data.map(d => [x(d.time.getTime()), y(wConv(d.wind || 0))]);
      const gp = data.map(d => [x(d.time.getTime()), y(wConv(Math.max(d.gust || 0, d.wind || 0)))]);
      // gusts = top edge of a band above the mean wind
      parts.push(<path key="wb" d={`${window.smoothPath(gp)} L ${wp[wp.length - 1][0]} ${wp[wp.length - 1][1]} ${window.smoothPath(wp.slice().reverse()).replace(/^M/, 'L')} Z`} className="fc-gust" />);
      parts.push(<path key="wl" d={window.smoothPath(wp)} fill="none" className="fc-wind" />);
    }
    if (L.humidity) {
      const y = scale('humidity', 0, 100);
      const hv = data.map(d => d.humidity || 0);
      laneFrame('humidity', 'VLHKOST %', [100], y, v => String(v), `${Math.round(Math.min(...hv))}–${Math.round(Math.max(...hv))} %`);
      parts.push(<path key="hl" d={window.smoothPath(data.map(d => [x(d.time.getTime()), y(d.humidity || 0)]))} fill="none" className="fc-hum" />);
    }
    let presY = null;
    if (L.pressure) {
      const pd = data.filter(d => d.pressure != null);
      const pv = pd.map(d => d.pressure);
      const lo = pv.length ? Math.min(...pv) : 1000, hi = pv.length ? Math.max(...pv) : 1020;
      const nt = window.niceTicks(lo, Math.max(hi, lo + 4), 3);
      const y = presY = scale('pressure', nt.lo, nt.hi);
      laneFrame('pressure', 'TLAK hPa', [nt.lo, nt.hi], y, v => fmtNum(v, 0),
        pv.length ? `${Math.round(lo)}–${Math.round(hi)} hPa` : 'bez údajů');
      if (pd.length > 1) parts.push(<path key="pl" d={window.smoothPath(pd.map(d => [x(d.time.getTime()), y(d.pressure)]))} fill="none" className="fc-pres" />);
    }
    // x axis: clock ticks
    const tickH = (t1 - t0) / 3600e3 > 48 ? 12 : (W < 600 ? 12 : 6);
    window.timeTicks(t0, t1, tickH).forEach(d => {
      if (d.getHours() === 0) return;
      parts.push(<text key={`x${d.getTime()}`} x={x(d.getTime())} y={H - 7} textAnchor="middle" className="fc-axis">{d.getHours()}:00</text>);
    });

    // y of each lane's value for the hover dots
    const yOf = {};
    if (L.temp) { const v = data.map(d => d.temp); const nt = window.niceTicks(Math.min(...v), Math.max(...v), Math.max(3, Math.min(6, Math.floor(L.temp.h / 26)))); yOf.temp = scale('temp', nt.lo, nt.hi); }
    if (L.wind) { const gm = Math.max(2, ...data.map(d => wConv(Math.max(d.gust || 0, d.wind || 0)))); yOf.wind = scale('wind', 0, window.niceTicks(0, gm, 3).hi); }
    if (L.humidity) yOf.humidity = scale('humidity', 0, 100);
    if (presY) yOf.pressure = presY;
    svg = (
      <>
        <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} className="fc-svg">
          {parts}
        </svg>
        <window.ChartHover W={W} H={H} top={padT} bottom={H - padB}
          xs={data.map(d => x(d.time.getTime()))}
          marks={(i) => [
            yOf.temp && { y: yOf.temp(data[i].temp), color: window.tempColor(data[i].temp, dark) },
            yOf.wind && { y: yOf.wind(wConv(data[i].wind || 0)), color: dark ? '#14b8a6' : '#0d9488' },
            yOf.humidity && { y: yOf.humidity(data[i].humidity || 0), color: dark ? '#22c55e' : '#16a34a' },
            yOf.pressure && data[i].pressure != null && { y: yOf.pressure(data[i].pressure), color: dark ? '#8b5cf6' : '#7c3aed' },
          ]}
          tip={(i) => {
            const hd = data[i];
            const row = (color, k, v, extra) => (
              <div className="tip-row">
                <span className="k"><i className="tip-sw" style={{ background: color }} />{k}</span>
                <strong>{v}{extra && <small>{extra}</small>}</strong>
              </div>
            );
            const foot = [hd.cloud != null && `oblačnost ${Math.round(hd.cloud)} %`, !config.pressure && hd.pressure && `${Math.round(hd.pressure)} hPa`].filter(Boolean);
            return (<>
              <div className="tip-head">
                {hd.icon && <img src={WeatherData.iconUrl(hd.icon)} alt="" width="32" height="32" />}
                <div>
                  <div className="tip-time">{window.fmtTipTime(hd.time)}</div>
                  {hd.cond && <div className="tip-cond">{hd.cond}</div>}
                </div>
              </div>
              {config.temp && row(window.tempColor(hd.temp, dark), 'Teplota', `${fmtNum(hd.temp, 1)} °C`)}
              {config.precip && row('#3b82f6', 'Srážky',
                hd.precip > 0.05 ? `${fmtNum(hd.precip, 1)} mm` : 'žádné',
                hd.precipProb != null ? `${hd.precipProb} %` : null)}
              {config.wind && row(dark ? '#14b8a6' : '#0d9488', 'Vítr',
                <>{fmtNum(wConv(hd.wind), 1)} {wUnit}{hd.windDeg != null && <i className="bi bi-arrow-up tip-dir" style={{ transform: `rotate(${hd.windDeg + 180}deg)` }} title={hd.windDir} />}{hd.windDir || ''}</>,
                hd.gust != null ? `nárazy ${fmtNum(wConv(hd.gust), 0)}` : null)}
              {config.humidity && row(dark ? '#22c55e' : '#16a34a', 'Vlhkost', `${fmtNum(hd.humidity)} %`)}
              {config.pressure && hd.pressure != null && row(dark ? '#8b5cf6' : '#7c3aed', 'Tlak', `${Math.round(hd.pressure)} hPa`)}
              {foot.length > 0 && <div className="tip-foot">{foot.join(' · ')}</div>}
            </>);
          }} />
      </>
    );
  }

  if (!data.length) return <div className="muted">Načítání…</div>;
  return (
    <div className="fc">
      {/* more quantities → a taller chart (each lane keeps a readable height) */}
      <div className="fc-plot" ref={plotRef} style={{ '--fc-min': `${120 + lanes.length * 64 + (config.icons ? 28 : 0)}px` }}>{svg}</div>
      {!lanes.length && <div className="muted">Zapni v nastavení widgetu aspoň jednu veličinu.</div>}
    </div>
  );
};
