/* global React, ReactDOM */
// =========================================================================
// APP — SOMNIA root, window manager, scene background, tweaks
// =========================================================================

const { useState, useEffect, useRef, useCallback } = React;
const { TweaksPanel, useTweaks, TweakSection, TweakSlider, TweakToggle, TweakRadio, TweakText } = window;
const MOBILE_QUERY = '(max-width: 760px)';

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "crt": true,
  "animation": "lively",
  "drowsy_threshold": 17,
  "awake_since": "07:30",
  "show_taskbar_clock": true,
  "show_sheep": true
}/*EDITMODE-END*/;

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const [booted, setBooted] = useState(false);
  const [windows, setWindows] = useState([]);
  const [zStack, setZStack] = useState([]);
  const [now, setNow] = useState(new Date());
  const [drowsyPopupVisible, setDrowsyPopupVisible] = useState(false);
  const drowsyTimerRef = useRef(null);
  const [showStart, setShowStart] = useState(false);
  const [isMobile, setIsMobile] = useState(() => (
    window.matchMedia ? window.matchMedia(MOBILE_QUERY).matches : window.innerWidth <= 760
  ));
  const [sleepSummary, setSleepSummary] = useState(() => (
    window.getSomniaSleepSummary
      ? window.getSomniaSleepSummary()
      : { rollingAvg3: null, isRedAlert: false, latestEntriesCount: 0, entriesCount: 0 }
  ));

  // SCENE state — drives desktop wallpaper + procedural audio
  const [activeScene, setActiveScene] = useState('night');
  const [soundOn, setSoundOn] = useState(false);
  const [volume, setVolume] = useState(0.2);

  // clock
  useEffect(() => {
    const iv = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(iv);
  }, []);

  useEffect(() => {
    const mq = window.matchMedia(MOBILE_QUERY);
    const update = () => setIsMobile(mq.matches);
    update();
    if (mq.addEventListener) {
      mq.addEventListener('change', update);
      return () => mq.removeEventListener('change', update);
    }
    mq.addListener(update);
    return () => mq.removeListener(update);
  }, []);

  // first-open welcome
  useEffect(() => {
    if (booted && windows.length === 0) {
      setTimeout(() => openApp('about'), 400);
    }
  }, [booted]);

  // Sync audio engine with state
  useEffect(() => {
    if (!soundOn) {
      window.playSceneAudio(null);
    } else {
      window.playSceneAudio(activeScene);
    }
  }, [activeScene, soundOn]);

  useEffect(() => {
    window.setMasterVolume(volume);
  }, [volume]);

  useEffect(() => {
    const updateSleepSummary = (event) => setSleepSummary(event.detail);
    window.addEventListener('somnia:sleep-summary', updateSleepSummary);
    if (window.getSomniaSleepSummary) setSleepSummary(window.getSomniaSleepSummary());
    return () => window.removeEventListener('somnia:sleep-summary', updateSleepSummary);
  }, []);

  useEffect(() => {
    if (!booted) return;
    const show = () => setDrowsyPopupVisible(true);
    show();
    drowsyTimerRef.current = setInterval(show, 5 * 60 * 1000);
    return () => clearInterval(drowsyTimerRef.current);
  }, [booted]);

  const dismissDrowsyPopup = () => setDrowsyPopupVisible(false);
  const openDrowsyDetail = () => {
    setDrowsyPopupVisible(false);
    openApp('redalert');
  };

  // calculate awake hours
  const awakeHours = (() => {
    const [h, m] = tweaks.awake_since.split(':').map(Number);
    const start = new Date(now);
    start.setHours(h, m, 0, 0);
    if (start > now) start.setDate(start.getDate() - 1);
    return Math.max(0, (now - start) / (1000 * 60 * 60));
  })();

  const drowsyPopupMessage = (() => {
    if (sleepSummary.isRedAlert) return 'Your 3-day average is critical. Click to learn why pushing through is unsafe.';
    if (sleepSummary.rollingAvg3 !== null) return `3-day avg: ${sleepSummary.rollingAvg3.toFixed(1)}h. Click for sleep safety tips.`;
    return 'Track your sleep to monitor drowsiness. Click for details.';
  })();

  // ── window manager ─────────────────────────────────────────────────────
  const getWindowFrame = useCallback((appConfig, index = 0) => {
    const viewportW = window.innerWidth || 1024;
    const viewportH = window.innerHeight || 768;
    const taskbarH = isMobile ? 52 : 56;
    const edgePad = isMobile ? 8 : 12;
    const offset = isMobile ? 0 : index * 28;
    const displayW = isMobile
      ? Math.max(300, viewportW - edgePad * 2)
      : Math.min(appConfig?.w || 540, Math.max(320, viewportW - 24));
    const displayH = isMobile
      ? Math.max(320, viewportH - taskbarH - edgePad * 2)
      : Math.min(appConfig?.h || 460, Math.max(240, viewportH - 68));
    const baseX = isMobile ? edgePad : 260 + offset;
    const baseY = isMobile ? edgePad : 60 + offset;
    const x = Math.max(edgePad, Math.min(baseX, viewportW - displayW - edgePad));
    const y = Math.max(edgePad, Math.min(baseY, viewportH - displayH - taskbarH));
    return { x, y, w: displayW, h: displayH };
  }, [isMobile]);

  const openApp = (id) => {
    setWindows(ws => {
      const existing = ws.find(w => w.id === id);
      if (existing) return ws.map(w => w.id === id ? { ...w, minimized: false } : w);
      const appConfig = window.APPS[id];
      const frame = getWindowFrame(appConfig, ws.length);
      const props = {};
      if (id === 'tracker') Object.assign(props, { awakeHours, drowsyThreshold: tweaks.drowsy_threshold });
      if (id === 'ambience') Object.assign(props, {
        activeScene, onPick: pickScene, soundOn, onSoundToggle: toggleSound,
        volume, onVolume: setVolume, animOn: tweaks.animation !== 'off'
      });
      return [...ws, { id, ...frame, minimized: false, props }];
    });
    setZStack(z => [...z.filter(x => x !== id), id]);
    setShowStart(false);
  };

  const closeWin = (id) => {
    setWindows(ws => ws.filter(w => w.id !== id));
    setZStack(z => z.filter(x => x !== id));
  };
  const minimizeWin = (id) => setWindows(ws => ws.map(w => w.id === id ? { ...w, minimized: true } : w));
  const focusWin = (id) => setZStack(z => [...z.filter(x => x !== id), id]);
  const moveWin = (id, pos) => setWindows(ws => ws.map(w => w.id === id ? { ...w, ...pos } : w));

  const toggleTaskbarTask = (id) => {
    const w = windows.find(w => w.id === id);
    if (!w) return;
    if (w.minimized) {
      setWindows(ws => ws.map(w => w.id === id ? { ...w, minimized: false } : w));
      focusWin(id);
    } else {
      const topId = zStack[zStack.length - 1];
      if (topId === id) minimizeWin(id);
      else focusWin(id);
    }
  };

  const pickScene = (id) => {
    window.unlockAudio();
    setActiveScene(id);
    if (!soundOn) setSoundOn(true); // turn on sound when picking a scene
  };

  const toggleSound = () => {
    if (!soundOn) window.unlockAudio();
    setSoundOn(s => !s);
  };

  // keep tracker + ambience window props live
  useEffect(() => {
    setWindows(ws => ws.map(w => {
      if (w.id === 'tracker') {
        return { ...w, props: { awakeHours, drowsyThreshold: tweaks.drowsy_threshold } };
      }
      if (w.id === 'ambience') {
        return { ...w, props: {
          activeScene, onPick: pickScene, soundOn, onSoundToggle: toggleSound,
          volume, onVolume: setVolume, animOn: tweaks.animation !== 'off'
        }};
      }
      return w;
    }));
  }, [awakeHours.toFixed(2), tweaks.drowsy_threshold, activeScene, soundOn, volume, tweaks.animation]);

  const fmtClock = (d) => d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
  const fmtDate = (d) => d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' }).toUpperCase();

  const sleepStatus = (() => {
    if (sleepSummary.isRedAlert) return { label: 'RED ALERT', color: 'var(--danger)' };
    if (awakeHours < 8) return { label: 'FRESH', color: 'var(--cyan)' };
    if (awakeHours < 14) return { label: 'STEADY', color: 'var(--moon)' };
    return { label: 'WANING', color: 'var(--rose)' };
  })();

  const animOn = tweaks.animation !== 'off';
  const sceneInfo = window.SCENES?.find(s => s.id === activeScene);

  return (
    <div
      className={`desktop ${tweaks.crt ? 'crt' : ''}`}
      data-scene={activeScene}
      data-anim={animOn ? 'on' : 'off'}>

      {!booted && <window.Boot onDone={() => setBooted(true)} />}

      {/* SCENE = WALLPAPER */}
      <div className="scene-bg">
        <window.MountainScene variant={activeScene} animOn={animOn} />
      </div>

      <window.JumpingSheep key={booted ? 'awake' : 'boot'} enabled={booted && tweaks.show_sheep && animOn} count={4} />

      {/* DESKTOP ICONS */}
      <div className="desktop-icons">
        {window.ICON_ORDER.map(id => (
          <window.DesktopIconSingle key={id} id={id} app={window.APPS[id]} onOpen={openApp} />
        ))}
      </div>

      {/* DROWSY POPUP */}
      {drowsyPopupVisible && (
        <div
          className={`drowsy-popup ${sleepSummary.isRedAlert ? 'red-alert' : ''}`}
          role="button"
          tabIndex={0}
          onClick={openDrowsyDetail}
          onKeyDown={(e) => {
            if (e.key === 'Enter' || e.key === ' ') {
              e.preventDefault();
              openDrowsyDetail();
            }
          }}>
          <button className="drowsy-popup-close" onClick={(e) => { e.stopPropagation(); dismissDrowsyPopup(); }} aria-label="Dismiss">X</button>
          <div className="drowsy-popup-icon">
            <window.IconAlert size={20} />
          </div>
          <div className="drowsy-popup-label">{sleepStatus.label}</div>
          <div className="drowsy-popup-value">
            {sleepSummary.rollingAvg3 !== null ? `${sleepSummary.rollingAvg3.toFixed(1)}h` : `${awakeHours.toFixed(1)}h`}
          </div>
          <div className="drowsy-popup-msg">{drowsyPopupMessage}</div>
        </div>
      )}

      {/* WINDOWS */}
      {windows.map(w => (
        <window.Win
          key={w.id}
          win={w}
          app={window.APPS[w.id]}
          z={100 + zStack.indexOf(w.id)}
          focused={zStack[zStack.length - 1] === w.id}
          onFocus={() => focusWin(w.id)}
          onClose={() => closeWin(w.id)}
          onMinimize={() => minimizeWin(w.id)}
          onMove={moveWin}
        />
      ))}

      {/* START MENU */}
      {showStart && (
        <div className="start-menu">
          <div className="start-menu-head">SOMNIA · MENU</div>
          {window.ICON_ORDER.concat('about').map(id => {
            const a = window.APPS[id];
            const Icon = window[a.icon];
            return (
              <button key={id} className="start-menu-item" onClick={() => openApp(id)}>
                <Icon size={20} />
                {a.label}
              </button>
            );
          })}
        </div>
      )}

      {/* TASKBAR */}
      <div className="taskbar">
        <button className="startbtn" onClick={() => setShowStart(s => !s)}>
          <img src="icons/favicon-48.png" width={18} height={18} alt="" style={{imageRendering: 'pixelated'}} /> SOMNIA
        </button>

        <div className="taskbar-tasks">
          {windows.map(w => {
            const a = window.APPS[w.id];
            const Icon = window[a.icon];
            const active = !w.minimized && zStack[zStack.length - 1] === w.id;
            return (
              <button key={w.id}
                className={`taskbar-task ${active ? 'active' : ''}`}
                onClick={() => toggleTaskbarTask(w.id)}>
                <div style={{width: 14, height: 14, flexShrink: 0}}>
                  <Icon size={14} />
                </div>
                {a.label}
              </button>
            );
          })}
        </div>

        <div className="tray">
          {/* Volume control */}
          <div className="tray-item tray-volume" title={soundOn ? 'Mute' : 'Unmute'}>
            <button className="tray-mute-btn" onClick={toggleSound}>
              {soundOn ? '♪' : '◌'}
            </button>
            <input
              type="range" min="0" max="1" step="0.05" value={volume}
              onPointerDown={() => soundOn && window.unlockAudio()}
              onChange={(e) => setVolume(parseFloat(e.target.value))}
              disabled={!soundOn}
              className="tray-volume-slider"
            />
            <span style={{minWidth: 26, fontSize: 8, color: soundOn ? 'var(--moon)' : 'var(--ink-faint)'}}>
              {soundOn ? `${Math.round(volume * 100)}%` : 'OFF'}
            </span>
          </div>

          <div style={{width: 2, height: 18, background: 'var(--chrome)'}} />

          {/* Scene indicator */}
          <div className="tray-item" title="Active scene">
            <span className="muted" style={{fontSize: 8}}>SCENE</span>
            <span className="accent-cyan" style={{fontSize: 9}}>{sceneInfo?.label || 'NIGHT'}</span>
          </div>

          <div style={{width: 2, height: 18, background: 'var(--chrome)'}} />

          {/* Drowsy meter */}
          <div className="tray-item">
            <window.IconAlert size={12} />
            <span style={{color: sleepStatus.color}}>{sleepStatus.label}</span>
            <span className="muted">
              {sleepSummary.rollingAvg3 === null ? `${awakeHours.toFixed(1)}H` : `${sleepSummary.rollingAvg3.toFixed(1)}H AVG`}
            </span>
          </div>

          <div style={{width: 2, height: 18, background: 'var(--chrome)'}} />

          {/* Clock */}
          <div className="tray-item">
            <span className="tray-zzz">Z</span>
            <span className="tray-clock">{fmtClock(now)}</span>
            {tweaks.show_taskbar_clock && <span className="muted">{fmtDate(now)}</span>}
          </div>
        </div>
      </div>

      {/* TWEAKS PANEL */}
      <TweaksPanel title="TWEAKS">
        <TweakSection label="Appearance">
          <TweakToggle label="CRT scanlines" value={tweaks.crt} onChange={(v) => setTweak('crt', v)} />
          <TweakRadio label="Animation"
            value={tweaks.animation}
            onChange={(v) => setTweak('animation', v)}
            options={[
              { value: 'off', label: 'Off' },
              { value: 'subtle', label: 'Subtle' },
              { value: 'lively', label: 'Lively' },
            ]} />
          <TweakToggle label="Jumping sheep"
            value={tweaks.show_sheep}
            onChange={(v) => setTweak('show_sheep', v)} />
        </TweakSection>

        <TweakSection label="Awake Meter">
          <TweakSlider label="Alert at"
            value={tweaks.drowsy_threshold}
            onChange={(v) => setTweak('drowsy_threshold', v)}
            min={10} max={24} step={1} unit="h" />
          <TweakText label="Awake since"
            value={tweaks.awake_since}
            onChange={(v) => setTweak('awake_since', v)} />
          <div style={{fontFamily: 'var(--font-mono)', fontSize: 14, color: 'var(--ink-dim)', marginTop: 8, padding: '0 4px'}}>
            {awakeHours.toFixed(1)}h awake · red alert uses tracker 3-day average
          </div>
        </TweakSection>

        <TweakSection label="System">
          <TweakToggle label="Date in taskbar"
            value={tweaks.show_taskbar_clock}
            onChange={(v) => setTweak('show_taskbar_clock', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
