// Reusable UI primitives + chart components for the DQI app.

const { useState, useEffect, useRef, useMemo, useCallback } = React;

// --- Banner ----------------------------------------------------------------
function Banner({ kind = "info", children }) {
  return <div className={`banner ${kind}`}>{children}</div>;
}

// --- Disclosure (mimics st.expander) ---------------------------------------
function Disclosure({ title, defaultOpen = false, children }) {
  const [open, setOpen] = useState(defaultOpen);
  return (
    <div className="disclosure">
      <div className="disclosure-summary" onClick={() => setOpen((o) => !o)}>
        <span className={`chev ${open ? "open" : ""}`}>▶</span>
        <span>{title}</span>
      </div>
      {open && <div className="disclosure-body">{children}</div>}
    </div>
  );
}

// --- DQI pill --------------------------------------------------------------
function DqiPill({ score }) {
  const cls = score >= 90 ? "good" : score >= 75 ? "warn" : "bad";
  return <span className={`dqi-pill ${cls}`}>DQI {score.toFixed(1)}%</span>;
}

// --- Geo tag ---------------------------------------------------------------
function GeoTag({ geoType }) {
  const cls = (geoType || "na").toLowerCase();
  return <span className={`geo-tag ${cls}`}>{geoType}</span>;
}

// --- Heatmap cell (table cell with red→green background) -------------------
function heatColor(val) {
  // val in 0..100. interpolate red(20) → yellow(60) → green(95)
  const v = Math.max(0, Math.min(100, val));
  // hue 0 (red) → 140 (green), light bg
  const hue = (v / 100) * 140;
  return `oklch(0.94 0.06 ${hue})`;
}
function HeatCell({ value, decimals = 1 }) {
  const v = Number.isFinite(value) ? value : 0;
  return (
    <span className="heat-cell" style={{ background: heatColor(v) }}>
      {v.toFixed(decimals)}
    </span>
  );
}

// --- Bar chart (single series) --------------------------------------------
function BarChart({ data, height = 220, yMax = 100, color = "var(--accent)" }) {
  // data: [{label, value}]
  const ref = useRef(null);
  const [w, setW] = useState(560);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver((entries) => {
      for (const e of entries) setW(Math.max(280, e.contentRect.width));
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  const padL = 44, padR = 14, padT = 14, padB = 42;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const n = Math.max(data.length, 1);
  const slot = innerW / n;
  const barW = Math.min(70, slot * 0.7);
  return (
    <div ref={ref} className="chart-block">
      <svg className="svg-chart" viewBox={`0 0 ${w} ${height}`}>
        {/* Y grid */}
        {[0, 25, 50, 75, 100].map((g) => {
          const y = padT + innerH - (g / yMax) * innerH;
          return (
            <g key={g}>
              <line x1={padL} x2={w - padR} y1={y} y2={y} stroke="var(--border)" strokeWidth="1" strokeDasharray={g === 0 ? "" : "3 3"} />
              <text x={padL - 8} y={y + 3} textAnchor="end" fontSize="10" fill="var(--ink-3)" fontFamily="Inter">{g}</text>
            </g>
          );
        })}
        {/* Bars */}
        {data.map((d, i) => {
          const cx = padL + slot * i + slot / 2;
          const h = (Math.max(0, d.value) / yMax) * innerH;
          const x = cx - barW / 2;
          const y = padT + innerH - h;
          return (
            <g key={i}>
              <rect x={x} y={y} width={barW} height={h} rx="3" fill={color} />
              <text x={cx} y={padT + innerH + 14} textAnchor="middle" fontSize="11" fill="var(--ink-2)" fontFamily="Inter">{d.label}</text>
              <text x={cx} y={y - 4} textAnchor="middle" fontSize="10" fill="var(--ink-2)" fontFamily="Inter" fontWeight="600">{d.value.toFixed(1)}</text>
            </g>
          );
        })}
      </svg>
    </div>
  );
}

// --- Grouped bar chart (multi-series) -------------------------------------
function GroupedBarChart({ groups, series, height = 280, yMax = 105, palette }) {
  // groups: [labels...], series: [{label, values:[...]}]
  const colors = palette || ["#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3", "#937860"];
  const ref = useRef(null);
  const [w, setW] = useState(560);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver((entries) => { for (const e of entries) setW(Math.max(320, e.contentRect.width)); });
    ro.observe(ref.current); return () => ro.disconnect();
  }, []);
  const padL = 44, padR = 14, padT = 14, padB = 64;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const nGroups = Math.max(groups.length, 1);
  const nSeries = Math.max(series.length, 1);
  const slot = innerW / nGroups;
  const groupBarsW = slot * 0.78;
  const barW = groupBarsW / nSeries;
  return (
    <div ref={ref} className="chart-block">
      <svg className="svg-chart" viewBox={`0 0 ${w} ${height}`}>
        {[0, 25, 50, 75, 100].map((g) => {
          const y = padT + innerH - (g / yMax) * innerH;
          return (
            <g key={g}>
              <line x1={padL} x2={w - padR} y1={y} y2={y} stroke="var(--border)" strokeWidth="1" strokeDasharray={g === 0 ? "" : "3 3"} />
              <text x={padL - 8} y={y + 3} textAnchor="end" fontSize="10" fill="var(--ink-3)" fontFamily="Inter">{g}</text>
            </g>
          );
        })}
        {groups.map((label, gi) => {
          const cx = padL + slot * gi + slot / 2;
          return (
            <g key={gi}>
              {series.map((s, si) => {
                const v = s.values[gi] ?? 0;
                const h = (Math.max(0, v) / yMax) * innerH;
                const x = cx - groupBarsW / 2 + si * barW;
                const y = padT + innerH - h;
                return (
                  <g key={si}>
                    <rect x={x} y={y} width={barW - 2} height={h} rx="2" fill={colors[si % colors.length]} />
                  </g>
                );
              })}
              <text x={cx} y={padT + innerH + 14} textAnchor="middle" fontSize="11" fill="var(--ink-2)" fontFamily="Inter">{label}</text>
            </g>
          );
        })}
        {/* Legend */}
        <g transform={`translate(${padL}, ${height - 26})`}>
          {series.map((s, si) => (
            <g key={si} transform={`translate(${si * 110}, 0)`}>
              <rect width="10" height="10" rx="2" fill={colors[si % colors.length]} />
              <text x="14" y="9" fontSize="11" fill="var(--ink-2)" fontFamily="Inter">{s.label}</text>
            </g>
          ))}
        </g>
      </svg>
    </div>
  );
}

// --- Line chart (multi-series) --------------------------------------------
function LineChart({ xLabels, series, height = 240, yMax = 105, palette }) {
  const colors = palette || ["#4C72B0", "#DD8452", "#55A868", "#C44E52"];
  const ref = useRef(null);
  const [w, setW] = useState(560);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver((entries) => { for (const e of entries) setW(Math.max(320, e.contentRect.width)); });
    ro.observe(ref.current); return () => ro.disconnect();
  }, []);
  const padL = 44, padR = 14, padT = 14, padB = 56;
  const innerW = w - padL - padR;
  const innerH = height - padT - padB;
  const n = Math.max(xLabels.length, 1);
  const xFor = (i) => padL + (n === 1 ? innerW / 2 : (i / (n - 1)) * innerW);
  const yFor = (v) => padT + innerH - (Math.max(0, v) / yMax) * innerH;
  return (
    <div ref={ref} className="chart-block">
      <svg className="svg-chart" viewBox={`0 0 ${w} ${height}`}>
        {[0, 25, 50, 75, 100].map((g) => {
          const y = padT + innerH - (g / yMax) * innerH;
          return (
            <g key={g}>
              <line x1={padL} x2={w - padR} y1={y} y2={y} stroke="var(--border)" strokeDasharray={g === 0 ? "" : "3 3"} />
              <text x={padL - 8} y={y + 3} textAnchor="end" fontSize="10" fill="var(--ink-3)" fontFamily="Inter">{g}</text>
            </g>
          );
        })}
        {series.map((s, si) => {
          const pts = s.values.map((v, i) => `${xFor(i)},${yFor(v)}`).join(" ");
          const color = colors[si % colors.length];
          return (
            <g key={si}>
              <polyline points={pts} fill="none" stroke={color} strokeWidth="2" />
              {s.values.map((v, i) => (
                <circle key={i} cx={xFor(i)} cy={yFor(v)} r="3" fill={color} />
              ))}
            </g>
          );
        })}
        {xLabels.map((lbl, i) => (
          <text key={i} x={xFor(i)} y={padT + innerH + 16} textAnchor="middle" fontSize="10" fill="var(--ink-2)" fontFamily="Inter" transform={`rotate(-25 ${xFor(i)} ${padT + innerH + 16})`}>{lbl}</text>
        ))}
        {series.length > 1 && (
          <g transform={`translate(${padL}, ${height - 16})`}>
            {series.map((s, si) => (
              <g key={si} transform={`translate(${si * 110}, 0)`}>
                <rect width="10" height="10" rx="2" fill={colors[si % colors.length]} />
                <text x="14" y="9" fontSize="11" fill="var(--ink-2)" fontFamily="Inter">{s.label}</text>
              </g>
            ))}
          </g>
        )}
      </svg>
    </div>
  );
}

Object.assign(window, { Banner, Disclosure, DqiPill, GeoTag, HeatCell, BarChart, GroupedBarChart, LineChart, heatColor });
