/* UI components: Brand, DayPicker (quick + bounded calendar), Filters (by region),
   AnimatedNumber, CameraList (km/h or region badge), MapStyleDropdown. */

/* ---------- Animated number ---------- */
function AnimatedNumber({ value, className }) {
  const [display, setDisplay] = React.useState(value);
  const fromRef = React.useRef(value);
  const rafRef = React.useRef(0);
  const toRef = React.useRef(value);
  React.useEffect(() => {
    const from = fromRef.current, to = value, start = performance.now(), dur = 480;
    toRef.current = to;
    cancelAnimationFrame(rafRef.current);
    const tick = (now) => {
      const p = Math.min(1, (now - start) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setDisplay(Math.round(from + (to - from) * e));
      if (p < 1) rafRef.current = requestAnimationFrame(tick);
      else fromRef.current = to;
    };
    rafRef.current = requestAnimationFrame(tick);
    // fallback: guarantee we land on the target even if rAF is throttled (background tab, etc.)
    const snap = setTimeout(() => { setDisplay(toRef.current); fromRef.current = toRef.current; }, dur + 60);
    return () => { cancelAnimationFrame(rafRef.current); clearTimeout(snap); };
  }, [value]);
  return React.createElement('span', { className }, display);
}

/* ---------- Brand ---------- */
function Brand() {
  return (
    <div className="brand">
      <div className="brand-mark"><span className="brand-pulse"></span><span className="brand-core"></span></div>
      <div className="brand-text">
        <div className="brand-name">SaCam<span>Alert</span></div>
        <div className="brand-sub">Adelaide mobile speed cameras</div>
      </div>
    </div>
  );
}

/* ---------- Mode toggle (Mobile vs Fixed cameras) ---------- */
function ModeToggle({ mode, onChange, compact }) {
  return (
    <div className={'mode-toggle' + (compact ? ' compact' : '')} role="tablist" aria-label="Camera type">
      <span className="mode-thumb" data-mode={mode}></span>
      <button className={'mode-opt' + (mode === 'mobile' ? ' on' : '')} role="tab"
        aria-selected={mode === 'mobile'} onClick={() => onChange('mobile')}>
        <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M14 17V6a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h1"/>
          <path d="M14 8h3.5a1 1 0 0 1 .8.4l2.5 3.3a1 1 0 0 1 .2.6V17a1 1 0 0 1-1 1h-1"/>
          <path d="M9 18h4"/>
          <circle cx="6.5" cy="18" r="1.8"/>
          <circle cx="16.5" cy="18" r="1.8"/>
        </svg>
        Mobile
      </button>
      <button className={'mode-opt' + (mode === 'fixed' ? ' on' : '')} role="tab"
        aria-selected={mode === 'fixed'} onClick={() => onChange('fixed')}>
        <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="7" width="18" height="11" rx="2"/><circle cx="12" cy="12.5" r="3"/><path d="M7 7l1.5-2.5h7L17 7"/></svg>
        Fixed
      </button>
    </div>
  );
}

/* ---------- Mini calendar (bounded to available days) ---------- */
function Calendar({ valueIso, todayIso, availableSet, onPick, onClose }) {
  const value = isoToDate(valueIso);
  const today = isoToDate(todayIso);
  const [view, setView] = React.useState(startOfDay(value));
  const year = view.getFullYear(), month = view.getMonth();
  const first = new Date(year, month, 1);
  const startPad = first.getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < startPad; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(year, month, d));
  const monthName = view.toLocaleDateString('en-AU', { month: 'long', year: 'numeric' });
  return (
    <div className="cal" onClick={(e) => e.stopPropagation()}>
      <div className="cal-head">
        <button className="cal-nav" onClick={() => setView(new Date(year, month - 1, 1))} aria-label="Previous month">‹</button>
        <span className="cal-title">{monthName}</span>
        <button className="cal-nav" onClick={() => setView(new Date(year, month + 1, 1))} aria-label="Next month">›</button>
      </div>
      <div className="cal-grid cal-dow">{['S','M','T','W','T','F','S'].map((d,i)=><span key={i} className="cal-dow-c">{d}</span>)}</div>
      <div className="cal-grid">
        {cells.map((c, i) => {
          if (c === null) return <span key={i} className="cal-cell empty"></span>;
          const iso = dateToIso(c);
          const avail = availableSet.has(iso);
          const cls = 'cal-cell'
            + (sameDate(c, value) ? ' sel' : '')
            + (sameDate(c, today) ? ' today' : '')
            + (avail ? '' : ' disabled');
          return (
            <button key={i} className={cls} disabled={!avail}
              onClick={() => { if (avail) { onPick(iso); onClose(); } }}>{c.getDate()}</button>
          );
        })}
      </div>
    </div>
  );
}

/* ---------- Day picker ---------- */
function DayPicker({ valueIso, todayIso, availableSet, onChange }) {
  const [calOpen, setCalOpen] = React.useState(false);
  const today = isoToDate(todayIso);
  const value = isoToDate(valueIso);
  const quick = [
    { label: 'Yesterday', iso: dateToIso(addDays(today, -1)) },
    { label: 'Today', iso: todayIso },
    { label: 'Tomorrow', iso: dateToIso(addDays(today, 1)) },
  ];
  React.useEffect(() => {
    if (!calOpen) return;
    const h = () => setCalOpen(false);
    window.addEventListener('click', h);
    return () => window.removeEventListener('click', h);
  }, [calOpen]);
  const isQuick = quick.some((q) => q.iso === valueIso);
  return (
    <div className="section">
      <div className="section-label">Choose a day</div>
      <div className="quick-row">
        {quick.map((q) => (
          <button key={q.label}
            className={'quick-pill' + (q.iso === valueIso ? ' active' : '')}
            disabled={!availableSet.has(q.iso)}
            onClick={() => onChange(q.iso)}>
            {q.label}
          </button>
        ))}
      </div>
      <div className="day-bar">
        <div className="day-readout">
          <span className="day-weekday">{value.toLocaleDateString('en-AU',{weekday:'long'})}</span>
          <span className="day-date">{value.toLocaleDateString('en-AU',{day:'numeric',month:'long',year:'numeric'})}</span>
        </div>
        <div className="cal-anchor">
          <button className={'cal-btn' + (!isQuick ? ' active' : '')} onClick={(e)=>{e.stopPropagation(); setCalOpen(o=>!o);}} aria-label="Open calendar">
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <rect x="3" y="4.5" width="18" height="16" rx="2.5"/><path d="M3 9h18M8 2.5v4M16 2.5v4"/>
            </svg>
          </button>
          {calOpen && <Calendar valueIso={valueIso} todayIso={todayIso} availableSet={availableSet}
            onPick={onChange} onClose={()=>setCalOpen(false)} />}
        </div>
      </div>
    </div>
  );
}

/* ---------- Filters (by region or camera type) ---------- */
function Filters({ regions, active, counts, onToggle, title }) {
  return (
    <div className="section">
      <div className="section-label">{title || 'Regions today'}</div>
      <div className="filter-list">
        {regions.map((r) => {
          const on = active.has(r.name);
          return (
            <button key={r.name}
              className={'filter-chip' + (on ? ' on' : '')}
              style={{ '--hue': r.hue }}
              onClick={() => onToggle(r.name)}>
              <span className="filter-swatch"><span className="filter-dot"></span></span>
              <span className="filter-meta">
                <span className="filter-name">{r.name}</span>
                <span className="filter-desc">{r.desc || 'Mobile patrol area'}</span>
              </span>
              <span className="filter-count"><AnimatedNumber value={counts[r.name] || 0} /></span>
              <span className="filter-check" aria-hidden="true">
                <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l5 5L20 6"/></svg>
              </span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

/* ---------- Camera list ---------- */
function CameraList({ cameras, hoveredId, selectedId, onHover, onSelect, dayKey }) {
  return (
    <div className="section list-section">
      <div className="section-label sticky">
        <span><AnimatedNumber value={cameras.length} /> cameras shown</span>
      </div>
      <div className="cam-list" key={dayKey}>
        {cameras.length === 0 && (
          <div className="empty-state">
            <div className="empty-dot"></div>
            No cameras match these filters for this day.
          </div>
        )}
        {cameras.map((cam, i) => {
          const isSel = cam.id === selectedId, isHov = cam.id === hoveredId;
          return (
            <button key={cam.id}
              className={'cam-row' + (isSel ? ' sel' : '') + (isHov ? ' hov' : '')}
              style={{ '--hue': cam.hue, animationDelay: (Math.min(i, 14) * 28) + 'ms' }}
              onMouseEnter={() => onHover(cam.id)}
              onMouseLeave={() => onHover(null)}
              onClick={() => onSelect(cam.id)}>
              <span className="cam-marker"><span className="cam-marker-core"></span></span>
              <span className="cam-info">
                <span className="cam-road">{cam.road}</span>
                <span className="cam-sub">{cam.suburb}{cam.suffix ? ' ' + cam.suffix : ''}</span>
              </span>
              {cam.limit
                ? <span className="cam-limit"><b>{cam.limit}</b><i>km/h</i></span>
                : cam.tag
                  ? <span className="cam-tag" style={{ '--hue': cam.hue }}>{cam.tag}</span>
                  : <span className="cam-unknown" title="Speed zone unknown">Speed zone unknown</span>}
            </button>
          );
        })}
      </div>
    </div>
  );
}

/* ---------- Map style dropdown ---------- */
function MapStyleDropdown({ value, onChange }) {
  const opts = [
    { id: 'midnight', label: 'Midnight' },
    { id: 'charcoal', label: 'Charcoal' },
    { id: 'streets', label: 'Streets' },
  ];
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const h = () => setOpen(false);
    window.addEventListener('click', h);
    return () => window.removeEventListener('click', h);
  }, [open]);
  const current = opts.find((o) => o.id === value) || opts[0];
  return (
    <div className="mapstyle" onClick={(e) => e.stopPropagation()}>
      <button className={'mapstyle-btn' + (open ? ' open' : '')} onClick={() => setOpen((o) => !o)}>
        <svg className="mapstyle-ic" viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" strokeLinecap="round">
          <path d="M9 3 3 5.5v15L9 18l6 3 6-2.5v-15L15 6 9 3Z"/><path d="M9 3v15M15 6v15"/>
        </svg>
        <span className="mapstyle-lbl">{current.label}</span>
        <svg className="mapstyle-chev" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9l6 6 6-6"/></svg>
      </button>
      {open && (
        <div className="mapstyle-menu">
          {opts.map((o) => (
            <button key={o.id} className={'mapstyle-opt' + (o.id === value ? ' sel' : '')}
              onClick={() => { onChange(o.id); setOpen(false); }}>
              <span className="mapstyle-swatch" data-style={o.id}></span>
              {o.label}
              {o.id === value && <span className="mapstyle-tick">✓</span>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

/* ---------- Install prompt (Add to Home Screen) ---------- */
function InstallPrompt() {
  const [show, setShow] = React.useState(false);
  const [mode, setMode] = React.useState(null);   // 'android' | 'ios'
  const [iosHint, setIosHint] = React.useState(false);

  React.useEffect(() => {
    const standalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches)
      || window.navigator.standalone;
    if (standalone) return;                       // already installed
    try {
      const d = +localStorage.getItem('installDismissed') || 0;
      if (Date.now() - d < 7 * 24 * 3600 * 1000) return;   // dismissed within a week
    } catch (e) {}
    const isIOS = /iphone|ipad|ipod/i.test(navigator.userAgent)
      || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); // iPadOS
    const decide = () => {
      if (window.__installPromptEvent) { setMode('android'); setShow(true); }
      else if (isIOS) { setMode('ios'); setShow(true); }
    };
    decide();
    const onAvail = () => { setMode('android'); setShow(true); };
    const onInstalled = () => setShow(false);
    window.addEventListener('installAvailable', onAvail);
    window.addEventListener('appInstalled', onInstalled);
    return () => {
      window.removeEventListener('installAvailable', onAvail);
      window.removeEventListener('appInstalled', onInstalled);
    };
  }, []);

  if (!show) return null;

  const dismiss = () => {
    try { localStorage.setItem('installDismissed', String(Date.now())); } catch (e) {}
    setShow(false); setIosHint(false);
  };
  const install = async () => {
    if (mode === 'android' && window.__installPromptEvent) {
      const e = window.__installPromptEvent;
      e.prompt();
      try { await e.userChoice; } catch (_) {}
      window.__installPromptEvent = null;
      setShow(false);
    } else {
      setIosHint(true);   // iOS: show the Share → Add to Home Screen hint
    }
  };

  return (
    <React.Fragment>
      <div className="install-card">
        <span className="install-ic">
          <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="6" y="2.5" width="12" height="19" rx="2.5"/><path d="M12 7v7m0 0l-2.5-2.5M12 14l2.5-2.5"/>
          </svg>
        </span>
        <span className="install-text">
          <b>Install SaCamAlert</b>
          <i>{mode === 'ios' ? 'Add it to your Home Screen' : 'One tap to add the app'}</i>
        </span>
        <button className="install-btn" onClick={install}>{mode === 'ios' ? 'How' : 'Install'}</button>
        <button className="install-x" onClick={dismiss} aria-label="Dismiss">×</button>
      </div>
      {iosHint && (
        <div className="ios-hint-scrim" onClick={() => setIosHint(false)}>
          <div className="ios-hint" onClick={(e) => e.stopPropagation()}>
            <span>Tap&nbsp;
              <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{verticalAlign:'-3px'}}>
                <path d="M12 15V3m0 0L8 7m4-4l4 4"/><path d="M5 12v7a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-7"/>
              </svg> (Share) at the bottom of Safari, then <b>“Add to Home Screen”</b>.
            </span>
            <span className="ios-hint-arrow"></span>
          </div>
        </div>
      )}
    </React.Fragment>
  );
}

Object.assign(window, { Brand, ModeToggle, DayPicker, Calendar, Filters, CameraList, AnimatedNumber, MapStyleDropdown, InstallPrompt });
