// TextiGreen v4 · shared UI primitives

const { useState: useSc4, useEffect: useEc4, useRef: useRc4 } = React;

// ─── Map API css color names → tasteful soft band colors ────────────
const BAND_COLOR = {
  green:       { solid: '#2C4A3A', soft: '#E8EDE9' },
  yellowgreen: { solid: '#6A8C5E', soft: '#EBEFE6' },
  gold:        { solid: '#B5A24A', soft: '#F2EFDF' },
  orange:      { solid: '#B87A3F', soft: '#F4EADF' },
  red:         { solid: '#9B3A2E', soft: '#F2E2DE' },
  grey:        { solid: '#9A988F', soft: '#EFEEE9' },
};
function bandColor(css) { return BAND_COLOR[css] || BAND_COLOR.grey; }

const CONF_LEGEND = 'A = high (official or peer-reviewed primary) · B = good · C = indicative · D = proxy / low-confidence';

// ─── Confidence badge — data quality, NOT a rating ──────────────────
const CONF_FILL = { 'A': 1, 'A-': 0.86, 'B+': 0.72, 'B': 0.58, 'B-': 0.46, 'C': 0.3, 'D+': 0.18, 'D': 0.12 };
function ConfidenceBadge({ value, dark = false, showLabel = false }) {
  const fill = CONF_FILL[value] != null ? CONF_FILL[value] : 0.3;
  const fg = dark ? 'var(--bg)' : 'var(--fg)';
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }} title={`Data confidence: ${value} · ${CONF_LEGEND}`}>
      <span style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        minWidth: 26, height: 21, padding: '0 6px', border: `1px solid ${fg}`,
        background: dark ? `rgba(255,255,255,${0.08 + fill * 0.14})` : `rgba(10,10,10,${0.02 + fill * 0.08})`,
        color: fg, fontFamily: 'var(--mono)', fontSize: 11, fontWeight: 600,
      }}>{value || '—'}</span>
      {showLabel && <span style={{ fontSize: 11, color: dark ? 'rgba(255,255,255,0.6)' : 'var(--muted)', fontFamily: 'var(--mono)' }}>confidence</span>}
    </span>
  );
}

// ─── Source chip — opens full citation + caveat ─────────────────────
function SourceChip({ id, dark = false, sources, caveat }) {
  const [open, setOpen] = useSc4(false);
  const ref = useRc4(null);
  useEc4(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);
  if (!id) return null;
  const cite = (sources || window.TGMock.SOURCES);
  const found = Array.isArray(cite) ? cite.find(s => s.source_id === id) : cite[id];
  const fg = dark ? 'var(--bg)' : 'var(--fg)';
  const border = dark ? 'rgba(255,255,255,0.32)' : 'var(--line)';
  return (
    <span ref={ref} style={{ display: 'inline-flex', position: 'relative' }}>
      <button type="button" onClick={() => setOpen(o => !o)} style={{
        display: 'inline-flex', alignItems: 'center', gap: 6, padding: '2px 8px',
        border: `1px solid ${border}`, background: 'transparent', color: fg, cursor: 'pointer',
        fontFamily: 'var(--mono)', fontSize: 11, letterSpacing: '0.02em', minHeight: 24,
      }}>
        <span style={{ opacity: 0.55 }}>src</span>{id}
        <span style={{ opacity: 0.45, fontSize: 9 }}>{open ? '▲' : '▼'}</span>
      </button>
      {open && (
        <span style={{
          position: 'absolute', top: '100%', left: 0, marginTop: 6, zIndex: 60, width: 280,
          padding: '12px 14px', background: 'var(--bg)', border: '1px solid var(--fg)',
          fontFamily: 'var(--sans)', fontSize: 12, color: 'var(--body)', lineHeight: 1.55,
          boxShadow: '0 8px 28px rgba(0,0,0,0.16)', textAlign: 'left',
        }}>
          <span style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
            <span style={{ fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--muted)' }}>{id}</span>
            {found && <ConfidenceBadge value={found.confidence_ceiling} />}
          </span>
          <span style={{ color: 'var(--fg)', display: 'block' }}>{found ? found.citation : 'Source reference.'}</span>
          {caveat && <span style={{ display: 'block', marginTop: 8, fontSize: 11, color: 'var(--muted)' }}>⚠ {caveat}</span>}
        </span>
      )}
    </span>
  );
}

// ─── Reference colour band (GWP only) — explicitly NOT a rating ──────
function ReferenceBand({ colorInterval, dark = false }) {
  if (!colorInterval) return null;
  const [css, label] = colorInterval;
  const c = bandColor(css);
  const order = ['green', 'yellowgreen', 'gold', 'orange', 'red'];
  const activeIdx = order.indexOf(css);
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
        <span style={{ width: 14, height: 14, borderRadius: '50%', background: c.solid, flexShrink: 0 }} />
        <span style={{ fontSize: 14, color: dark ? 'var(--bg)' : 'var(--fg)', fontWeight: 500 }}>{label}</span>
        <span style={{ marginLeft: 'auto', fontFamily: 'var(--mono)', fontSize: 10, letterSpacing: '0.06em', color: dark ? 'rgba(255,255,255,0.5)' : 'var(--muted)', textTransform: 'uppercase' }}>
          reference band · not a rating
        </span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5,1fr)', gap: 3 }}>
        {order.map((k, i) => {
          const active = i === activeIdx;
          const kc = bandColor(k);
          return (
            <div key={k} style={{
              height: 8,
              background: active ? kc.solid : (dark ? 'rgba(255,255,255,0.14)' : kc.soft),
              boxShadow: active ? (dark ? 'inset 0 0 0 1px var(--bg)' : 'inset 0 0 0 1px rgba(0,0,0,0.25)') : 'none',
            }} />
          );
        })}
      </div>
    </div>
  );
}

// ─── min–max range bar ──────────────────────────────────────────────
function RangeBar({ value, min, max, dark = false }) {
  if (min == null || max == null) return null;
  const sm = Math.max(max * 1.12, 0.5);
  const lo = (min / sm) * 100, hi = (max / sm) * 100, v = (value / sm) * 100;
  const track = dark ? 'rgba(255,255,255,0.12)' : 'var(--surface-2)';
  const fill = dark ? 'var(--accent-light)' : 'var(--accent-light)';
  const mark = dark ? 'var(--bg)' : 'var(--accent-strong)';
  const txt = dark ? 'rgba(255,255,255,0.6)' : 'var(--muted)';
  return (
    <div>
      <div style={{ position: 'relative', height: 6, background: track, marginBottom: 6 }}>
        <div style={{ position: 'absolute', left: `${lo}%`, width: `${hi - lo}%`, top: 0, bottom: 0, background: fill }} />
        <div style={{ position: 'absolute', left: `${v}%`, top: -3, bottom: -3, width: 2, background: mark, transform: 'translateX(-1px)' }} />
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--mono)', fontSize: 10, color: txt }}>
        <span>min {min.toFixed(min < 10 ? 2 : 1)}</span>
        <span style={{ color: dark ? 'var(--bg)' : 'var(--fg)' }}>{value.toFixed(value < 10 ? 2 : 1)}</span>
        <span>max {max.toFixed(max < 10 ? 2 : 1)}</span>
      </div>
    </div>
  );
}

// ─── Plain-language reasons for unapplied corrections ───────────────
function humanizeReason(r) {
  if (!r) return 'not applied';
  if (/no process.?electricity/i.test(r)) return "Grid adjustment doesn't apply at this stage (no separable electricity data).";
  if (/no.*thermal|no.*heat|no steam/i.test(r)) return "Thermal adjustment doesn't apply at this stage (no separable heat data).";
  if (/out of (scope|boundary)|not.*scope/i.test(r)) return "Not applicable within this boundary.";
  return r;
}

// ─── Correction delta (re-base, not add-on) ─────────────────────────
function CorrectionRow({ title, correction, dark = false }) {
  const fg = dark ? 'var(--bg)' : 'var(--fg)';
  const muted = dark ? 'rgba(255,255,255,0.55)' : 'var(--muted)';
  if (!correction) return null;
  const applied = correction.applied;
  const delta = correction.total_delta_per_kg != null ? correction.total_delta_per_kg : correction.delta_per_kg;
  return (
    <div style={{ padding: '12px 0', borderBottom: `1px solid ${dark ? 'rgba(255,255,255,0.14)' : 'var(--line)'}` }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12 }}>
        <span style={{ fontSize: 13, color: fg, fontWeight: 500 }}>{title}</span>
        {applied ? (
          <span style={{ fontFamily: 'var(--mono)', fontSize: 14, color: delta > 0 ? 'var(--warn-fg)' : delta < 0 ? 'var(--accent)' : muted, fontVariantNumeric: 'tabular-nums' }}>
            {delta > 0 ? '+' : ''}{(delta || 0).toFixed(3)}
          </span>
        ) : (
          <span style={{ fontFamily: 'var(--mono)', fontSize: 11, color: muted, fontStyle: 'italic' }}>not applied</span>
        )}
      </div>
      <div style={{ fontSize: 11, color: muted, marginTop: 4, lineHeight: 1.5 }}>
        {applied ? (correction.note || 'Re-base applied.') : humanizeReason(correction.reason)}
      </div>
    </div>
  );
}

// ─── Warning bar ────────────────────────────────────────────────────
function WarningBar({ children, tone = 'caveat', dark = false }) {
  const palette = {
    caveat: { bar: dark ? 'rgba(255,255,255,0.4)' : 'var(--muted)', txt: dark ? 'rgba(255,255,255,0.8)' : 'var(--muted)' },
    warn:   { bar: 'var(--band-orange)', txt: dark ? 'var(--bg)' : 'var(--body)' },
    strong: { bar: 'var(--band-red)', txt: dark ? 'var(--bg)' : 'var(--body)' },
  }[tone];
  return (
    <div style={{
      display: 'flex', gap: 10, padding: '10px 12px',
      background: dark ? 'rgba(255,255,255,0.06)' : 'var(--surface)',
      borderLeft: `3px solid ${palette.bar}`, fontSize: 12, lineHeight: 1.55, color: palette.txt,
    }}>
      <span style={{ flex: 1 }}>{children}</span>
    </div>
  );
}

// ─── Skeleton ───────────────────────────────────────────────────────
function Skeleton({ height = 16, width = '100%', dark = false, style }) {
  return <div style={{ height, width, background: dark
    ? 'linear-gradient(90deg, rgba(255,255,255,0.08), rgba(255,255,255,0.16), rgba(255,255,255,0.08))'
    : 'linear-gradient(90deg, var(--surface), var(--surface-2), var(--surface))',
    backgroundSize: '200% 100%', animation: 'tg4Shimmer 1.2s ease-in-out infinite', ...style }} />;
}

// ─── Offline tag ────────────────────────────────────────────────────
function OfflineTag({ dark = false, label = 'mock data', danger = false }) {
  const color = danger ? 'var(--band-red)' : (dark ? 'rgba(255,255,255,0.6)' : 'var(--muted)');
  const borderColor = danger ? 'var(--band-red)' : (dark ? 'rgba(255,255,255,0.35)' : 'var(--line)');
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'var(--mono)', fontSize: 10,
      letterSpacing: '0.06em', color, border: `1px dashed ${borderColor}`, padding: '2px 7px',
    }}>◌ {label}</span>
  );
}

// ─── Field label ────────────────────────────────────────────────────
// ─── Hover/focus info tooltip (the small "i" next to a label) ───────
function InfoDot({ text, dark = false }) {
  const [open, setOpen] = useSc4(false);
  if (!text) return null;
  return (
    <span style={{ position: 'relative', display: 'inline-flex', marginLeft: 6, verticalAlign: 'middle' }}
      onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
      <span tabIndex={0} role="button" aria-label="More info"
        onFocus={() => setOpen(true)} onBlur={() => setOpen(false)}
        style={{
          width: 15, height: 15, borderRadius: '50%', display: 'inline-flex',
          alignItems: 'center', justifyContent: 'center',
          border: `1px solid ${dark ? 'rgba(255,255,255,0.5)' : 'var(--muted)'}`,
          color: dark ? 'rgba(255,255,255,0.7)' : 'var(--muted)',
          fontSize: 10, lineHeight: 1, cursor: 'help', fontFamily: 'var(--sans)',
          textTransform: 'none', letterSpacing: 0, fontWeight: 700, fontStyle: 'italic',
        }}>i</span>
      {open && (
        <span style={{
          position: 'absolute', top: '100%', left: '50%', transform: 'translateX(-50%)',
          marginTop: 8, zIndex: 80, width: 248, padding: '11px 13px', background: 'var(--bg)',
          border: '1px solid var(--fg)', fontFamily: 'var(--sans)', fontSize: 12, fontWeight: 400,
          color: 'var(--body)', lineHeight: 1.5, textTransform: 'none', letterSpacing: 0,
          whiteSpace: 'normal', textAlign: 'left', boxShadow: '0 8px 28px rgba(0,0,0,0.16)',
        }}>{text}</span>
      )}
    </span>
  );
}

function Label({ children, hint, n, info }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 10 }}>
      <span style={{ fontSize: 11, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--muted)', fontFamily: 'var(--mono)' }}>
        {n != null && <span style={{ marginRight: 8, opacity: 0.6 }}>{String(n).padStart(2, '0')}</span>}{children}
        {info && <InfoDot text={info} />}
      </span>
      {hint && <span style={{ fontSize: 11, color: 'var(--muted)' }}>{hint}</span>}
    </div>
  );
}

// ─── Segmented control ──────────────────────────────────────────────
function Segmented({ options, value, onChange, compact }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${options.length},1fr)`, border: '1px solid var(--fg)', background: 'var(--bg)' }}>
      {options.map((o, i) => {
        const active = o.value === value;
        return (
          <button key={o.value} type="button" onClick={() => onChange(o.value)} style={{
            padding: compact ? '10px 6px' : '13px 8px', border: 'none',
            borderLeft: i > 0 ? '1px solid var(--fg)' : 'none',
            background: active ? 'var(--fg)' : 'transparent', color: active ? 'var(--bg)' : 'var(--fg)',
            fontSize: compact ? 12 : 13, fontWeight: active ? 600 : 400, cursor: 'pointer',
            minHeight: 44, whiteSpace: 'nowrap', fontFamily: 'var(--sans)', transition: 'background 0.12s',
          }}>{o.label}</button>
        );
      })}
    </div>
  );
}

// ─── Plain select ───────────────────────────────────────────────────
function Select({ value, onChange, children, placeholder }) {
  return (
    <select value={value || ''} onChange={e => onChange(e.target.value)} style={{
      width: '100%', padding: '12px 36px 12px 14px', border: '1px solid var(--line)', background: 'var(--bg)',
      color: 'var(--fg)', fontSize: 14, fontFamily: 'var(--sans)', cursor: 'pointer', minHeight: 44, appearance: 'none',
      backgroundImage: "url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23000' fill='none' stroke-width='1.2'/></svg>\")",
      backgroundRepeat: 'no-repeat', backgroundPosition: 'right 14px center',
    }}>
      {placeholder && <option value="">{placeholder}</option>}
      {children}
    </select>
  );
}

// ─── Searchable select (for origin) ─────────────────────────────────
function SearchSelect({ value, onChange, groups, placeholder = 'Search…' }) {
  // groups: [{ label, items: [{ value, label, sub }] }]
  const [open, setOpen] = useSc4(false);
  const [q, setQ] = useSc4('');
  const ref = useRc4(null);
  useEc4(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); setQ(''); } };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, [open]);

  const allItems = groups.flatMap(g => g.items);
  const current = allItems.find(i => i.value === value);
  const ql = q.trim().toLowerCase();
  const filtered = groups.map(g => ({
    label: g.label,
    items: g.items.filter(i => !ql || i.label.toLowerCase().includes(ql) || (i.sub || '').toLowerCase().includes(ql)),
  })).filter(g => g.items.length);

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button type="button" onClick={() => setOpen(o => !o)} style={{
        width: '100%', padding: '12px 36px 12px 14px', border: '1px solid var(--line)', background: 'var(--bg)',
        color: current ? 'var(--fg)' : 'var(--muted)', fontSize: 14, fontFamily: 'var(--sans)', cursor: 'pointer',
        minHeight: 44, textAlign: 'left', position: 'relative',
      }}>
        {current ? current.label : placeholder}
        <span style={{ position: 'absolute', right: 14, top: '50%', transform: 'translateY(-50%)', fontSize: 10, color: 'var(--muted)' }}>{open ? '▲' : '▼'}</span>
      </button>
      {open && (
        <div style={{ position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4, zIndex: 70, background: 'var(--bg)', border: '1px solid var(--fg)', boxShadow: '0 10px 30px rgba(0,0,0,0.16)', maxHeight: 320, overflowY: 'auto' }}>
          <div style={{ padding: 8, borderBottom: '1px solid var(--line)', position: 'sticky', top: 0, background: 'var(--bg)' }}>
            <input autoFocus value={q} onChange={e => setQ(e.target.value)} placeholder="Type to filter…" style={{
              width: '100%', padding: '9px 10px', border: '1px solid var(--line)', background: 'var(--bg)',
              fontSize: 13, fontFamily: 'var(--sans)', color: 'var(--fg)', minHeight: 40,
            }} />
          </div>
          {filtered.length === 0 && <div style={{ padding: 14, fontSize: 13, color: 'var(--muted)' }}>No matches.</div>}
          {filtered.map(g => (
            <div key={g.label}>
              <div style={{ padding: '8px 14px 4px', fontSize: 10, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--muted)', fontFamily: 'var(--mono)' }}>{g.label}</div>
              {g.items.map(it => (
                <button key={it.value} type="button" onClick={() => { onChange(it.value); setOpen(false); setQ(''); }} style={{
                  width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10,
                  padding: '11px 14px', border: 'none', background: it.value === value ? 'var(--surface)' : 'transparent',
                  color: 'var(--fg)', fontSize: 13, cursor: 'pointer', textAlign: 'left', fontFamily: 'var(--sans)', minHeight: 42,
                }}>
                  <span>{it.label}</span>
                  {it.sub && <span style={{ fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--muted)' }}>{it.sub}</span>}
                </button>
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Disclosure ─────────────────────────────────────────────────────
function Disclosure({ title, defaultOpen = false, children }) {
  const [open, setOpen] = useSc4(defaultOpen);
  return (
    <div style={{ borderTop: '1px solid var(--line)' }}>
      <button type="button" onClick={() => setOpen(o => !o)} style={{
        width: '100%', padding: '14px 0', background: 'transparent', border: 'none',
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        fontSize: 12, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--fg)',
        cursor: 'pointer', fontFamily: 'var(--mono)',
      }}>
        <span>{title}</span>
        <span style={{ transform: open ? 'rotate(45deg)' : 'none', transition: 'transform 0.18s', width: 14, textAlign: 'center' }}>+</span>
      </button>
      {open && <div style={{ padding: '2px 0 18px', fontSize: 13, color: 'var(--body)', lineHeight: 1.65 }}>{children}</div>}
    </div>
  );
}

// ─── Logo ───────────────────────────────────────────────────────────
function Logo() {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
      <span style={{ width: 15, height: 15, background: 'var(--accent)', borderRadius: '50%' }} />
      <span style={{ fontSize: 16, fontWeight: 600, fontFamily: 'var(--display)', color: 'var(--fg)', letterSpacing: '-0.01em' }}>TextiGreen</span>
    </span>
  );
}

// ─── Connection pill ────────────────────────────────────────────────
function ConnectionPill({ conn, onClick }) {
  const map = {
    online: { dot: 'var(--accent)', txt: 'Live', sub: 'backend' },
    offline: { dot: 'var(--band-orange)', txt: 'Offline', sub: 'mock' },
    checking: { dot: 'var(--muted)', txt: 'Checking', sub: '' },
    unknown: { dot: 'var(--line)', txt: '—', sub: '' },
  };
  const s = conn.mode === 'offline' ? map.offline : (map[conn.status] || map.unknown);
  return (
    <button type="button" onClick={onClick} title="API connection settings" style={{
      display: 'inline-flex', alignItems: 'center', gap: 8, padding: '6px 10px', minHeight: 34,
      border: '1px solid var(--line)', background: 'var(--bg)', cursor: 'pointer',
      fontFamily: 'var(--mono)', fontSize: 11, letterSpacing: '0.03em', color: 'var(--fg)',
    }}>
      <span style={{ width: 8, height: 8, borderRadius: '50%', background: s.dot, animation: conn.status === 'checking' ? 'tg4Pulse 1s ease-in-out infinite' : 'none' }} />
      <span>{s.txt}</span>{s.sub && <span style={{ opacity: 0.5 }}>· {s.sub}</span>}
      <span style={{ opacity: 0.4 }}>⚙</span>
    </button>
  );
}

Object.assign(window, {
  bandColor, ConfidenceBadge, SourceChip, ReferenceBand, RangeBar, CorrectionRow,
  WarningBar, Skeleton, OfflineTag, Label, Segmented, Select, SearchSelect, Disclosure, Logo, ConnectionPill,
  humanizeReason,
});
