// Solkart — daily production curve
// Shows the smoothed PV power curve from midnight Oslo through the end of
// available forecast data, with a live "now" marker. Click a fylke on the
// map to focus the chart on that fylke; click again to return to all of Norge.
const { useEffect, useMemo, useRef, useState } = React;

// Round a number up to a "nice" axis-friendly maximum.
function niceCeil(v) {
  if (v <= 0) return 1;
  const exp = Math.floor(Math.log10(v));
  const base = Math.pow(10, exp);
  const m = v / base;
  let nice;
  if (m <= 1) nice = 1;
  else if (m <= 1.5) nice = 1.5;
  else if (m <= 2) nice = 2;
  else if (m <= 3) nice = 3;
  else if (m <= 5) nice = 5;
  else if (m <= 7.5) nice = 7.5;
  else nice = 10;
  return nice * base;
}

function fmtPower(kW) {
  if (kW >= 1000) return (kW / 1000).toFixed(kW >= 10000 ? 0 : 1) + " MW";
  return Math.round(kW).toLocaleString("nb-NO") + " kW";
}

function fmtOsloHour(date) {
  return new Intl.DateTimeFormat("nb-NO", {
    timeZone: "Europe/Oslo", hour: "2-digit", minute: "2-digit",
  }).format(date);
}

// Sample the smooth area-preserving curve at a uniform step from midnight
// over the next 24 h. Returns an array of { ms, kW }.
function sampleCurve(midnightMs, fylkeId, stepMs) {
  const out = [];
  const endMs = midnightMs + 24 * 3600000;
  for (let ms = midnightMs; ms <= endMs; ms += stepMs) {
    const w = window.SOLKART_SIM.instantPower(new Date(ms), fylkeId || null);
    out.push({ ms, kW: w / 1000 });
  }
  return out;
}

function ProductionChart({ now, selectedFylke, fylkeLabel, currentKW, onClear }) {
  // Logical SVG canvas — width-flexible via viewBox.
  const W = 1000, H = 280;
  const padL = 56, padR = 24, padT = 18, padB = 36;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;

  const midnight = window.SOLKART_SIM.osloMidnight(now);
  const midnightMs = midnight.getTime();
  const dayEndMs = midnightMs + 24 * 3600000;

  // The curve is computed from forecast data (changes only when forecast
  // refreshes — every 15 min). Tie the memo to a coarse minute key so we
  // don't resample every second when `now` ticks.
  const minuteKey = Math.floor(now.getTime() / 60000);
  const samples = useMemo(
    () => sampleCurve(midnightMs, selectedFylke, 5 * 60 * 1000),
    [midnightMs, selectedFylke, minuteKey]
  );

  // Hourly average values for picking out the day's peak.
  const hourlyKey = `${minuteKey}-${selectedFylke || ""}`;
  const hourly = useMemo(
    () => window.SOLKART_SIM.dailyEntries(selectedFylke || null)
      .filter(e => e.ms >= midnightMs && e.ms < dayEndMs),
    [hourlyKey, midnightMs, dayEndMs]
  );

  // Y-axis max — leave a bit of headroom so the curve doesn't kiss the top.
  const peakKW = Math.max(0, ...samples.map(s => s.kW), ...hourly.map(h => h.kW));
  const yMaxKW = niceCeil(Math.max(peakKW * 1.08, 1));

  // Geometry helpers
  const xFor = (ms) => padL + ((ms - midnightMs) / (24 * 3600000)) * innerW;
  const yFor = (kW) => padT + innerH - (Math.min(kW, yMaxKW) / yMaxKW) * innerH;

  // Build the area + line paths
  const linePath = samples.length === 0 ? "" : samples
    .map((s, i) => `${i === 0 ? "M" : "L"} ${xFor(s.ms).toFixed(1)} ${yFor(s.kW).toFixed(1)}`)
    .join(" ");
  const areaPath = samples.length === 0 ? "" :
    `${linePath} L ${xFor(samples[samples.length - 1].ms).toFixed(1)} ${yFor(0).toFixed(1)} L ${xFor(samples[0].ms).toFixed(1)} ${yFor(0).toFixed(1)} Z`;

  // "Now" position
  const nowMs = now.getTime();
  const inDay = nowMs >= midnightMs && nowMs <= dayEndMs;
  const nowX = inDay ? xFor(nowMs) : null;
  const nowY = inDay ? yFor(currentKW) : null;

  // Peak hour annotation
  const peak = hourly.length > 0
    ? hourly.reduce((b, e) => (e.kW > b.kW ? e : b), hourly[0])
    : null;

  // Hover state — read in viewBox coords, snap to nearest sample.
  const svgRef = useRef(null);
  const [hover, setHover] = useState(null); // { x, kW, ms } in viewBox coords

  function handleMove(e) {
    if (!svgRef.current || samples.length === 0) return;
    const rect = svgRef.current.getBoundingClientRect();
    const px = (e.clientX - rect.left) * (W / rect.width);
    if (px < padL || px > W - padR) { setHover(null); return; }
    // Find nearest sample by x
    let bestI = 0, bestDx = Infinity;
    for (let i = 0; i < samples.length; i++) {
      const dx = Math.abs(xFor(samples[i].ms) - px);
      if (dx < bestDx) { bestDx = dx; bestI = i; }
    }
    const s = samples[bestI];
    setHover({ x: xFor(s.ms), y: yFor(s.kW), kW: s.kW, ms: s.ms });
  }
  function handleLeave() { setHover(null); }

  // Hour tick positions (every 3 h)
  const hourTicks = [];
  for (let h = 0; h <= 24; h += 3) {
    hourTicks.push({ h, x: xFor(midnightMs + h * 3600000) });
  }
  // Y gridlines at 0, 25, 50, 75, 100% of yMax
  const yGrid = [0, 0.25, 0.5, 0.75, 1].map(f => ({
    f,
    y: yFor(yMaxKW * f),
    label: fmtPower(yMaxKW * f),
  }));

  const title = selectedFylke ? `Solproduksjon i ${fylkeLabel} i dag` : "Solproduksjon i Norge i dag";

  return (
    <div className="chart-card">
      <div className="chart-header">
        <div>
          <div className="kicker dark"><LivePulseInline /> Sanntidskurve</div>
          <h3 className="chart-title">{title}</h3>
        </div>
        <div className="chart-stats">
          {peak && peak.kW > 0 && (
            <div className="chart-stat">
              <span className="chart-stat__label">Topp i dag</span>
              <span className="chart-stat__val">{fmtPower(peak.kW)}</span>
              <span className="chart-stat__sub">kl {fmtOsloHour(new Date(peak.ms + 1800000))}</span>
            </div>
          )}
          {selectedFylke && (
            <button className="chart-back" onClick={onClear} title="Tilbake til Norge">
              ← Hele Norge
            </button>
          )}
        </div>
      </div>

      <svg
        ref={svgRef}
        viewBox={`0 0 ${W} ${H}`}
        className="chart-svg"
        onMouseMove={handleMove}
        onMouseLeave={handleLeave}
        onTouchMove={(e) => { if (e.touches[0]) handleMove(e.touches[0]); }}
        onTouchEnd={handleLeave}
      >
        <defs>
          <linearGradient id="chart-fill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="oklch(0.82 0.18 60)" stopOpacity="0.55" />
            <stop offset="55%" stopColor="oklch(0.88 0.14 75)" stopOpacity="0.30" />
            <stop offset="100%" stopColor="oklch(0.92 0.10 85)" stopOpacity="0" />
          </linearGradient>
          <linearGradient id="chart-line" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="oklch(0.62 0.20 35)" />
            <stop offset="100%" stopColor="oklch(0.78 0.18 70)" />
          </linearGradient>
        </defs>

        {/* Y gridlines + labels */}
        {yGrid.map((g, i) => (
          <g key={i}>
            <line
              x1={padL} x2={W - padR} y1={g.y} y2={g.y}
              stroke="oklch(0.85 0.02 80)"
              strokeWidth={i === 0 ? 1.2 : 0.8}
              strokeDasharray={i === 0 ? "" : "3 4"}
              opacity={i === 0 ? 0.9 : 0.5}
            />
            <text
              x={padL - 8} y={g.y + 4}
              textAnchor="end"
              fontFamily="'DM Sans', system-ui, sans-serif"
              fontSize="11"
              fill="oklch(0.55 0.04 60)"
            >
              {i === 0 ? "0" : g.label}
            </text>
          </g>
        ))}

        {/* Hour ticks */}
        {hourTicks.map(({ h, x }) => (
          <g key={h}>
            <line
              x1={x} x2={x}
              y1={padT + innerH} y2={padT + innerH + 4}
              stroke="oklch(0.7 0.03 80)" strokeWidth="0.8"
            />
            <text
              x={x} y={H - 14}
              textAnchor="middle"
              fontFamily="'DM Sans', system-ui, sans-serif"
              fontSize="11"
              fill="oklch(0.5 0.04 60)"
            >
              {String(h).padStart(2, "0")}
            </text>
          </g>
        ))}

        {/* Area + line */}
        {areaPath && <path d={areaPath} fill="url(#chart-fill)" />}
        {linePath && <path d={linePath} fill="none" stroke="url(#chart-line)" strokeWidth="2.4" strokeLinejoin="round" strokeLinecap="round" />}

        {/* Past/future divider — past is filled solid, future is faded */}
        {nowX != null && (
          <rect
            x={nowX} y={padT}
            width={W - padR - nowX} height={innerH}
            fill="oklch(0.99 0.005 80)" opacity="0.55"
            pointerEvents="none"
          />
        )}

        {/* Now line + dot */}
        {nowX != null && (
          <g pointerEvents="none">
            <line
              x1={nowX} x2={nowX}
              y1={padT} y2={padT + innerH}
              stroke="oklch(0.55 0.20 35)" strokeWidth="1.6"
              strokeDasharray="4 3"
              opacity="0.8"
            />
            <circle cx={nowX} cy={nowY} r="6" fill="oklch(0.6 0.22 30)" stroke="oklch(0.99 0.01 80)" strokeWidth="2" />
            <circle cx={nowX} cy={nowY} r="11" fill="none" stroke="oklch(0.6 0.22 30)" strokeWidth="1.5" opacity="0.4">
              <animate attributeName="r" values="6;14;6" dur="1.8s" repeatCount="indefinite" />
              <animate attributeName="opacity" values="0.55;0;0.55" dur="1.8s" repeatCount="indefinite" />
            </circle>
            {/* "Now" pill */}
            <NowPill x={nowX} y={padT + 4} kW={currentKW} time={fmtOsloHour(now)} W={W} padR={padR} />
          </g>
        )}

        {/* Hover crosshair */}
        {hover && (
          <g pointerEvents="none">
            <line
              x1={hover.x} x2={hover.x}
              y1={padT} y2={padT + innerH}
              stroke="oklch(0.45 0.05 60)" strokeWidth="0.8"
              strokeDasharray="2 3" opacity="0.7"
            />
            <circle cx={hover.x} cy={hover.y} r="4.5" fill="oklch(0.99 0.01 80)" stroke="oklch(0.4 0.08 50)" strokeWidth="1.5" />
            <HoverPill x={hover.x} y={hover.y} kW={hover.kW} time={fmtOsloHour(new Date(hover.ms))} W={W} padR={padR} padL={padL} />
          </g>
        )}
      </svg>
    </div>
  );
}

// Floating label near the "now" line.
function NowPill({ x, y, kW, time, W, padR }) {
  const text = `${fmtPower(kW)} · kl ${time}`;
  const approxWidth = Math.min(220, 70 + text.length * 6.2);
  const flipLeft = x + approxWidth + 14 > W - padR;
  const px = flipLeft ? x - approxWidth - 8 : x + 8;
  return (
    <g>
      <rect x={px} y={y} width={approxWidth} height="26" rx="13"
        fill="oklch(0.6 0.22 30)" opacity="0.96" />
      <text
        x={px + approxWidth / 2} y={y + 17}
        textAnchor="middle"
        fontFamily="'DM Sans', system-ui, sans-serif"
        fontSize="12.5" fontWeight="600"
        fill="oklch(0.99 0.01 80)"
      >{text}</text>
    </g>
  );
}

function HoverPill({ x, y, kW, time, W, padR, padL }) {
  const text = `${fmtPower(kW)} · kl ${time}`;
  const w = Math.min(200, 60 + text.length * 6.4);
  const tx = Math.max(padL, Math.min(W - padR - w, x - w / 2));
  const ty = Math.max(20, y - 30);
  return (
    <g>
      <rect x={tx} y={ty} width={w} height="22" rx="11"
        fill="oklch(0.25 0.05 60)" opacity="0.94" />
      <text
        x={tx + w / 2} y={ty + 15}
        textAnchor="middle"
        fontFamily="'DM Sans', system-ui, sans-serif"
        fontSize="11.5" fontWeight="500"
        fill="oklch(0.98 0.005 80)"
      >{text}</text>
    </g>
  );
}

function LivePulseInline() {
  return (
    <span style={{ position: "relative", display: "inline-block", width: 8, height: 8, marginRight: 4 }}>
      <span style={{ position: "absolute", inset: 0, borderRadius: "50%", background: "oklch(0.6 0.22 30)" }} />
      <span style={{
        position: "absolute", inset: -2, borderRadius: "50%",
        border: "2px solid oklch(0.6 0.22 30)",
        animation: "solkart-pulse 1.6s ease-out infinite",
      }} />
    </span>
  );
}

window.ProductionChart = ProductionChart;
