/* global React */
/* ============== Cassette Selection ===================================
   A vertical semicircle of cassette tapes on the right of the chassis.
   - Cassettes are small at rest, scale up on hover.
   - Click a cassette: it animates into the floppy slot of the chassis
     and the song begins. The CRT screen is NOT changed (boot splash
     still visible — clicking the screen will boot the OS as before).
   - Click a different cassette while one is playing: the loaded tape
     ejects (returns to its arc position), then the new tape inserts.
   - Click the floppy slot on the chassis to eject the current tape.
   - When the OS boots while audio is playing, a music indicator
     appears in the menu bar.
   ===================================================================== */

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

const CASSETTES = [
  {
    id: 'sweet-dreams',
    title: 'Sweet Dreams',
    artist: 'Eurythmics',
    duration: '3:37',
    color: '#d4a574', ink: '#2a1810',
    side: 'A · 01',
    cover: 'img/cassettes/sweet-dreams.jpg',
    src: 'audio/sweet-dreams.mp3',
  },
  {
    id: 'places-to-be',
    title: 'places to be',
    artist: 'Fred again..',
    duration: '3:46',
    color: '#6b8db8', ink: '#0a0e22',
    side: 'B · 02',
    cover: 'img/cassettes/places-to-be.jpg',
    src: 'audio/places-to-be.mp3',
  },
  {
    id: 'my-favorite-part',
    title: 'My Favorite Part',
    artist: 'Mac Miller · Ariana Grande',
    duration: '3:36',
    color: '#d8a4b5', ink: '#3a1828',
    side: 'A · 03',
    cover: 'img/cassettes/my-favorite-part.jpg',
    src: 'audio/my-favorite-part.mp3',
  },
  {
    id: 'these-nights',
    title: 'These Nights',
    artist: '88rising · Rich Brian · CHUNG HA',
    duration: '3:43',
    color: '#c47a4a', ink: '#1a0808',
    side: 'B · 04',
    cover: 'img/cassettes/these-nights.jpg',
    src: 'audio/these-nights.mp3',
  },
  {
    id: 'heartless',
    title: 'Heartless',
    artist: 'Kanye West',
    duration: '3:31',
    color: '#2a2a2a', ink: '#e8e1d3',
    side: 'A · 05',
    cover: 'img/cassettes/heartless.jpg',
    src: 'audio/heartless.mp3',
  },
];

/* Vertical arc geometry: cassettes stack vertically on the right side of
   the screen with EQUAL vertical spacing (linear y), bowing outward to the
   right at the extremes (concave faces right, away from the computer). */
function arcPosition(index, total) {
  const t = total === 1 ? 0.5 : index / (total - 1);
  const tCenter = t * 2 - 1;          // -1 (top) → +1 (bottom)
  const radius = 200;                  // vertical half-extent (compact arc)
  const bow = 44;                      // horizontal bow at the extremes
  // Linear y so cassettes are evenly spaced from each other; cos-based x so
  // the arc still reads as a smooth crescent.
  const y = tCenter * radius;
  const x = (1 - Math.cos(tCenter * Math.PI / 2)) * bow;
  // Tilt: top tilts forward, bottom backward.
  const tilt = tCenter * 13.5;
  return { x, y, tilt };
}

/* ========== Single cassette tape ========================================ª */
function Cassette({ data, index, total, onPick, hovered, setHovered, loaded, ejecting }) {
  const { x, y, tilt } = arcPosition(index, total);
  const isHover = hovered === data.id;
  const isOther = hovered && hovered !== data.id;
  const ref = useRef(null);
  const [insertTransform, setInsertTransform] = useState(null);

  // When this cassette becomes loaded, compute slot position in the arc's
  // local coordinate space (so the loaded transform is independent of the
  // cassette's prior arc slot — needed when others redistribute on load).
  useLayoutEffect(() => {
    if (!loaded) { setInsertTransform(null); return; }
    if (!ref.current) return;
    const slot = document.querySelector('.chassis-floppy');
    const arcEl = ref.current.parentElement; // .cassette-arc
    if (!slot || !arcEl) return;
    const slotR = slot.getBoundingClientRect();
    const arcR = arcEl.getBoundingClientRect();
    const slotCx = slotR.left + slotR.width / 2;
    const slotCy = slotR.top + slotR.height / 2;
    // The loaded transform pivots at the cassette's bottom (origin 50% 100%)
    // and squashes vertically to scale 0.04. So the post-scale visual center
    // sits halfHeight × (1 − scaleY) above where ty would put the pre-scale
    // center. Subtract that gap so the sliver lands centered in the slot.
    const halfH = ref.current.offsetHeight / 2;
    const sliverOffset = halfH * (1 - 0.04);
    setInsertTransform({
      tx: slotCx - arcR.left,
      ty: slotCy - arcR.top - sliverOffset,
    });
  }, [loaded]);

  // At rest, cassettes are small. Hover scales up. Loaded tape hides.
  const restScale = 0.68;
  const hoverScale = 1.18;
  const scale = isHover ? hoverScale : restScale;

  let transform;
  if (loaded && insertTransform) {
    transform = `translate(${insertTransform.tx}px, ${insertTransform.ty}px) rotate(0deg) scale(0.34, 0.04)`;
  } else if (loaded) {
    // Pre-measurement frame: keep at home so the transition starts cleanly.
    transform = `translate(${x}px, ${y}px) rotate(${tilt}deg) scale(${restScale})`;
  } else {
    transform = `translate(${x}px, ${y}px) rotate(${tilt}deg) scale(${scale})`;
  }

  return (
    <div
      ref={ref}
      className={`cassette-slot ${loaded ? 'loaded' : ''} ${ejecting === data.id ? 'ejecting' : ''} ${isOther ? 'dim' : ''}`}
      style={{
        transform,
        zIndex: loaded ? 200 : (isHover ? 100 : 40 + Math.abs(index - (total-1)/2)),
      }}
      onMouseEnter={() => setHovered(data.id)}
      onMouseLeave={() => setHovered(null)}
      onClick={() => onPick(data)}
    >
      <div className="cassette" style={{ '--cs-color': data.color, '--cs-ink': data.ink }}>
        <div className="cs-label">
          <div className="cs-label-head">
            <span className="cs-side">{data.side}</span>
            <span className="cs-brand">{data.duration}</span>
          </div>
          <div className="cs-title">{data.title}</div>
          <div className="cs-artist">{data.artist}</div>
        </div>
        {data.cover && (
          <div className="cs-sticker" style={{ backgroundImage: `url("${data.cover}")` }} aria-hidden="true"/>
        )}
        <div className="cs-window">
          <div className="cs-tape"/>
          <div className="cs-reel left"><div className="cs-reel-spokes"/></div>
          <div className="cs-reel right"><div className="cs-reel-spokes"/></div>
        </div>
        <span className="cs-screw tl"/>
        <span className="cs-screw tr"/>
        <span className="cs-screw bl"/>
        <span className="cs-screw br"/>
        <div className="cs-holes"><span/><span/></div>
      </div>
      <div className={`cassette-name ${isHover && !loaded ? 'show' : ''}`}>{data.title}</div>
    </div>
  );
}

/* ========== Picker stage =============================================== */
function CassetteStage({ activeCassetteId, onPick, onEject }) {
  const [hovered, setHovered] = useState(null);

  const handlePick = useCallback((data) => {
    if (data.id === activeCassetteId) {
      // Clicking the loaded cassette = no-op (eject is via the slot)
      return;
    }
    onPick(data);
  }, [activeCassetteId, onPick]);

  // Non-loaded cassettes redistribute across the full arc so there's no gap
  // where the loaded one used to sit. The loaded cassette keeps its original
  // index so its fly-to-slot transition starts from a stable spot.
  const visible = CASSETTES.filter(c => c.id !== activeCassetteId);

  return (
    <div className="cassette-stage">
      <div className="cassette-arc">
        {CASSETTES.map((c, i) => {
          const loaded = c.id === activeCassetteId;
          const visIdx = visible.findIndex(v => v.id === c.id);
          return (
            <Cassette
              key={c.id}
              data={c}
              index={loaded ? i : visIdx}
              total={loaded ? CASSETTES.length : visible.length}
              onPick={handlePick}
              hovered={hovered}
              setHovered={setHovered}
              loaded={loaded}
            />
          );
        })}
      </div>
    </div>
  );
}

/* ========== Tiny player indicator for the OS menu bar =================
   State (paused/progress/duration) is owned by the App-level useAudio hook
   and passed in as props so the same indicator works regardless of which
   shell instantiates it. */
function MusicIndicator({ data, paused, progress, duration, onTogglePlay, onEject }) {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    if (!open) return;
    const close = (e) => { if (!e.target.closest('.music-indicator-pop')) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  if (!data) return null;
  const fmt = (t) => {
    if (!t || isNaN(t)) return '0:00';
    const m = Math.floor(t / 60);
    const s = Math.floor(t % 60).toString().padStart(2, '0');
    return `${m}:${s}`;
  };
  const pct = duration ? (progress / duration) * 100 : 0;

  return (
    <div className="music-indicator-wrap">
      <div className={`music-indicator ${paused ? '' : 'playing'}`}
           onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }}>
        <div className="mi-bars">
          <span/><span/><span/>
        </div>
        <span className="mi-title">{data.title}</span>
      </div>
      {open && (
        <div className="music-indicator-pop" onClick={(e) => e.stopPropagation()}>
          <div className="mip-row">
            <div
              className="mip-art"
              style={data.cover
                ? { backgroundImage: `url("${data.cover}")`, backgroundSize: 'cover', backgroundPosition: 'center' }
                : { background: data.color }}
            />
            <div className="mip-meta">
              <div className="mip-title">{data.title}</div>
              <div className="mip-sub">{data.artist} · {data.side}</div>
            </div>
          </div>
          <div className="mip-bar"><div className="mip-fill" style={{ width: `${pct}%`, background: data.color }}/></div>
          <div className="mip-times">
            <span>{fmt(progress)}</span><span>{fmt(duration)}</span>
          </div>
          <div className="mip-controls">
            <button className="mip-btn" onClick={onTogglePlay}>
              {paused ? '▶ Play' : '❚❚ Pause'}
            </button>
            <button className="mip-btn mip-eject" onClick={onEject}>⏏ Eject</button>
          </div>
        </div>
      )}
    </div>
  );
}

window.CassetteStage = CassetteStage;
window.MusicIndicator = MusicIndicator;
window.CASSETTES = CASSETTES;
