/* global React,
   IOSStatusBar, IOSHomeScreen,
   IOSWorkApp, IOSSideApp, IOSWritingApp,
   IOSAboutApp, IOSResumeApp, IOSContactsApp,
   IOSSettingsApp, IOSMusicApp,
   useClock */

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

/* App open/close animation timings — kept short so they feel snappy. */
const APP_OPEN_MS  = 320;
const APP_CLOSE_MS = 260;

/* ============== Power flow for the phone ==============================
   Phases: locked → unlocking → home.
   locked:    small phone, lock screen with clock + interactive slider.
   unlocking: chassis scales up + lock screen fades / blurs out + home zooms in.
   home:      iOS chrome, chassis at fullscreen scale.
   The slider is the only entry point — there's no separate click-to-wake. */
const UNLOCK_DURATION_MS = 800;

function usePhonePower(skip) {
  const [phase, setPhase] = useState(skip ? 'home' : 'locked');
  const timers = useRef([]);

  useEffect(() => {
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = '';
      timers.current.forEach(clearTimeout);
    };
  }, []);

  useEffect(() => { if (skip) setPhase('home'); }, [skip]);

  const unlock = useCallback(() => {
    setPhase(p => {
      if (p !== 'locked') return p;
      timers.current.forEach(clearTimeout);
      timers.current = [];
      timers.current.push(setTimeout(() => setPhase('home'), UNLOCK_DURATION_MS));
      return 'unlocking';
    });
  }, []);

  const sleep = useCallback(() => {
    timers.current.forEach(clearTimeout);
    timers.current = [];
    setPhase('locked');
  }, []);

  return { phase, unlock, sleep };
}

/* ============== Lock screen + slide-to-unlock ========================== */
const DAYS   = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const MONTHS = ['January','February','March','April','May','June','July','August',
                'September','October','November','December'];

function LockScreen({ onUnlock, interactive }) {
  const now = useClock();
  let h = now.getHours();
  const m = now.getMinutes().toString().padStart(2, '0');
  h = h % 12; if (h === 0) h = 12;
  const dateLine = `${DAYS[now.getDay()]}, ${MONTHS[now.getMonth()]} ${now.getDate()}`;

  return (
    <div className="ios-lock">
      <IOSStatusBar/>
      <div className="ios-lock-clock">
        <div className="ios-lock-time">{h}:{m}</div>
        <div className="ios-lock-date">{dateLine}</div>
      </div>
      <div className="ios-lock-spacer"/>
      <SlideToUnlock onUnlock={onUnlock} interactive={interactive}/>
    </div>
  );
}

function SlideToUnlock({ onUnlock, interactive }) {
  const trackRef = useRef(null);
  const [dx, setDx] = useState(0);
  const [dragging, setDragging] = useState(false);
  const [unlocked, setUnlocked] = useState(false);
  const finishedRef = useRef(false);

  // If the slider becomes non-interactive (we're transitioning away), keep
  // its state — a knob frozen at the end of the track reads as "successfully
  // unlocked" while the lock layer fades out.
  useEffect(() => {
    if (interactive) {
      finishedRef.current = false;
    }
  }, [interactive]);

  const onPointerDown = (e) => {
    if (!interactive || unlocked || finishedRef.current) return;
    e.preventDefault();
    e.stopPropagation();
    const track = trackRef.current;
    if (!track) return;
    const startX = e.clientX;
    const KNOB_W = 64, PAD = 4;
    const maxDx = Math.max(0, track.clientWidth - KNOB_W - PAD * 2);
    setDragging(true);

    const move = (ev) => {
      const ndx = Math.max(0, Math.min(maxDx, ev.clientX - startX));
      setDx(ndx);
    };
    const end = (ev) => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', end);
      window.removeEventListener('pointercancel', end);
      setDragging(false);
      const finalDx = Math.max(0, Math.min(maxDx, ev.clientX - startX));
      const threshold = maxDx * 0.85;
      if (finalDx >= threshold) {
        finishedRef.current = true;
        setDx(maxDx);
        setUnlocked(true);
        // Brief settle, then trigger the unlock transition.
        setTimeout(onUnlock, 180);
      } else {
        setDx(0);
      }
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', end);
    window.addEventListener('pointercancel', end);
  };

  return (
    <div ref={trackRef}
         className={`ios-slide-track ${dragging ? 'dragging' : ''} ${unlocked ? 'unlocked' : ''}`}>
      <div className="ios-slide-text">slide to unlock</div>
      <div className="ios-slide-knob"
           style={{ transform: `translateX(${dx}px)` }}
           onPointerDown={onPointerDown}>
        <div className="ios-slide-arrow"/>
      </div>
    </div>
  );
}

/* ============== Phone chassis (iPhone 3GS look) ======================== */
function PhoneChassis({ phase, onWakeButton, onHomeButton, children }) {
  return (
    <div className="phone-stage">
      <div className={`phone-wrap phase-${phase}`}>
        <div className="phone-body">
          <div className="phone-bezel"/>
          <div className="phone-earpiece"/>
          <div className="phone-camera"/>
          <div className="phone-screen">
            {children}
          </div>
          <div className="phone-home"
               title="Home"
               onClick={(e) => { e.stopPropagation(); onHomeButton?.(); }}>
            <div className="phone-home-glyph"/>
          </div>
        </div>
        <div className="phone-wake"
             title="Wake / Sleep"
             onClick={(e) => { e.stopPropagation(); onWakeButton?.(); }}/>
        <div className="phone-ringer"/>
        <div className="phone-vol-up"/>
        <div className="phone-vol-down"/>
      </div>
    </div>
  );
}

/* ============== PhoneShell — main mobile experience ===================
   Route shape: { app, stack, anim, origin }
   - app:   active app id, or null when on the springboard.
   - stack: nav stack inside the app (push/pop for detail views).
   - anim:  null | 'opening' | 'closing'. While set, the springboard and the
            app shell coexist so the open/close zoom from the icon position
            can play. Inputs are gated until anim clears.
   - origin: { x, y } — fraction of the canvas where the launching icon sat,
            used as transform-origin for the zoom (icon → fullscreen → icon). */
function PhoneShell({ tweaks, setTweak, audioApi }) {
  const { phase, unlock, sleep } = usePhonePower(tweaks.skipIntro);
  const [route, setRoute] = useState({ app: null, stack: [], anim: null, origin: null });
  const animTimer = useRef(null);

  useEffect(() => () => { if (animTimer.current) clearTimeout(animTimer.current); }, []);

  const openApp = useCallback((appId, origin) => {
    setRoute(r => {
      if (r.anim || r.app) return r;
      if (animTimer.current) clearTimeout(animTimer.current);
      animTimer.current = setTimeout(() => {
        setRoute(r2 => r2.anim === 'opening' ? { ...r2, anim: null } : r2);
      }, APP_OPEN_MS);
      return { app: appId, stack: [], anim: 'opening', origin: origin || { x: 0.5, y: 0.5 } };
    });
  }, []);

  const pushDetail = useCallback((item) => {
    setRoute(r => ({ ...r, stack: [...r.stack, item] }));
  }, []);
  const popDetail = useCallback(() => {
    setRoute(r => ({ ...r, stack: r.stack.slice(0, -1) }));
  }, []);

  const goHome = useCallback(() => {
    setRoute(r => {
      if (!r.app || r.anim === 'closing') return r;
      if (animTimer.current) clearTimeout(animTimer.current);
      animTimer.current = setTimeout(() => {
        setRoute({ app: null, stack: [], anim: null, origin: null });
      }, APP_CLOSE_MS);
      return { ...r, anim: 'closing' };
    });
  }, []);

  // Wake button: only meaningful at 'home' (locks the phone). At 'locked' /
  // 'unlocking' it's a no-op since the screen is already showing the lock UI
  // or the unlock animation is mid-flight. Sleep skips animations — the
  // screen's about to be black anyway.
  const onWakeButton = useCallback(() => {
    if (phase !== 'home') return;
    if (animTimer.current) clearTimeout(animTimer.current);
    setRoute({ app: null, stack: [], anim: null, origin: null });
    sleep();
  }, [phase, sleep]);

  // Home button: at 'home', exit the current app with the close animation.
  // At 'locked' it's no-op — the slider is the only way in.
  const onHomeButton = useCallback(() => {
    if (phase === 'home') goHome();
  }, [phase, goHome]);

  // Settings → Restart re-locks the phone (no animation needed).
  const restart = useCallback(() => {
    if (animTimer.current) clearTimeout(animTimer.current);
    setRoute({ app: null, stack: [], anim: null, origin: null });
    sleep();
  }, [sleep]);

  // While animating, render both the springboard (behind, dimmed) and the
  // app shell (front, scaling). Otherwise render whichever is current.
  const showSpringboard = !route.app || route.anim === 'opening' || route.anim === 'closing';
  const showApp = !!route.app;
  const animClass = route.anim ? `is-${route.anim}` : '';
  const frameStyle = route.origin ? {
    '--app-orig-x': `${(route.origin.x * 100).toFixed(2)}%`,
    '--app-orig-y': `${(route.origin.y * 100).toFixed(2)}%`,
  } : undefined;

  // Home content — same node during 'home' and the 'unlocking' crossfade so
  // the springboard isn't remounted between the two.
  const homeContent = (
    <div className="ios-screen-canvas">
      <IOSStatusBar/>
      <div className="ios-stage">
        {showSpringboard && (
          <div className={`ios-springboard ${animClass}`}>
            <IOSHomeScreen onOpen={openApp}/>
          </div>
        )}
        {showApp && (
          <div className={`ios-app-frame ${animClass}`} style={frameStyle}>
            {renderIOSApp(route.app, route.stack, pushDetail, popDetail,
                          audioApi, tweaks, setTweak, restart)}
          </div>
        )}
      </div>
    </div>
  );

  // Layers visible per phase. Keys keep the lock layer (and its slider state)
  // alive across the locked → unlocking transition so the knob stays at the
  // end of the track while the layer fades out.
  const showHome = phase === 'home' || phase === 'unlocking';
  const showLock = phase === 'locked' || phase === 'unlocking';

  const layers = (
    <>
      {showHome && (
        <div key="home-layer"
             className={phase === 'unlocking' ? 'ios-unlock-home' : 'ios-home-direct'}>
          {homeContent}
        </div>
      )}
      {showLock && (
        <div key="lock-layer"
             className={phase === 'unlocking' ? 'ios-unlock-lock' : 'ios-lock-direct'}>
          <LockScreen onUnlock={unlock} interactive={phase === 'locked'}/>
        </div>
      )}
    </>
  );

  return (
    <PhoneChassis phase={phase}
                  onWakeButton={onWakeButton}
                  onHomeButton={onHomeButton}>
      {layers}
    </PhoneChassis>
  );
}

function renderIOSApp(appId, stack, push, pop, audioApi, tweaks, setTweak, restart) {
  switch (appId) {
    case 'work':     return <IOSWorkApp     stack={stack} onPushDetail={push} onPopDetail={pop}/>;
    case 'side':     return <IOSSideApp     stack={stack} onPushDetail={push} onPopDetail={pop}/>;
    case 'writing':  return <IOSWritingApp  stack={stack} onPushDetail={push} onPopDetail={pop}/>;
    case 'about':    return <IOSAboutApp/>;
    case 'browser':  return <IOSResumeApp/>;
    case 'contacts': return <IOSContactsApp/>;
    case 'settings': return <IOSSettingsApp tweaks={tweaks} setTweak={setTweak} onRestart={restart}/>;
    case 'music':    return <IOSMusicApp    stack={stack} onPushDetail={push} onPopDetail={pop}
                                            audioApi={audioApi}/>;
    default:         return null;
  }
}

window.PhoneShell = PhoneShell;
