// ============================================================
// Spotify browser panel — search, liked, playlists, recent
// Handoff: user picks a Spotify track → resolve to YouTube → queue it
// ============================================================

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

function SpotifyPanel({ onAddToQueue, onClose }) {
  const [connected, setConnected] = useState(SpotifyClient.isConnected());
  const [me, setMe] = useState(null);
  const [tab, setTab] = useState('search'); // search | liked | playlists | recent
  const [query, setQuery] = useState('');
  const [searchResults, setSearchResults] = useState([]);
  const [liked, setLiked] = useState(null);
  const [playlists, setPlaylists] = useState(null);
  const [openPlaylist, setOpenPlaylist] = useState(null); // { id, name, tracks }
  const [recent, setRecent] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [audioFeatures, setAudioFeatures] = useState({}); // spotifyId → { bpm, key }
  const [handoffRow, setHandoffRow] = useState(null); // { track, best, alternatives }
  const [resolvingId, setResolvingId] = useState(null);
  const [sortBy, setSortBy] = useState('default'); // default | bpm | key

  // Load profile after connect
  useEffect(() => {
    if (!connected) { setMe(null); return; }
    SpotifyClient.getMe().then(setMe).catch(() => setConnected(false));
  }, [connected]);

  // Handle login callback on first mount
  useEffect(() => {
    (async () => {
      const picked = await SpotifyClient.handleRedirectCallback();
      if (picked) setConnected(true);
    })();
  }, []);

  // --- Data fetchers ---
  const runSearch = useCallback(async (q) => {
    if (!q.trim()) { setSearchResults([]); return; }
    setLoading(true); setError(null);
    try {
      const results = await SpotifyClient.search(q, 25);
      setSearchResults(results);
      enrichFeatures(results);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, []);

  const loadLiked = useCallback(async () => {
    if (liked) return;
    setLoading(true); setError(null);
    try {
      const data = await SpotifyClient.getLikedSongs(50);
      setLiked(data);
      enrichFeatures(data.items);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, [liked]);

  const loadPlaylists = useCallback(async () => {
    if (playlists) return;
    setLoading(true); setError(null);
    try { setPlaylists(await SpotifyClient.getPlaylists(50)); }
    catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, [playlists]);

  const openPlaylistDetail = useCallback(async (p) => {
    setOpenPlaylist({ ...p, tracks: null });
    setLoading(true); setError(null);
    try {
      const data = await SpotifyClient.getPlaylistTracks(p.id, 100);
      setOpenPlaylist({ ...p, tracks: data.items });
      enrichFeatures(data.items);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, []);

  const loadRecent = useCallback(async () => {
    if (recent) return;
    setLoading(true); setError(null);
    try {
      const data = await SpotifyClient.getRecentlyPlayed(50);
      setRecent(data);
      enrichFeatures(data.items);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, [recent]);

  // Batch-fetch BPM/key for a page of tracks
  const enrichFeatures = useCallback(async (tracks) => {
    if (!tracks?.length) return;
    const ids = tracks.map(t => t.spotifyId).filter(id => id && !audioFeatures[id]);
    if (!ids.length) return;
    try {
      const feats = await SpotifyClient.getAudioFeatures(ids);
      setAudioFeatures(prev => ({ ...prev, ...feats }));
    } catch {}
  }, [audioFeatures]);

  // Tab switching
  useEffect(() => {
    if (!connected) return;
    if (tab === 'liked') loadLiked();
    else if (tab === 'playlists') loadPlaylists();
    else if (tab === 'recent') loadRecent();
  }, [tab, connected, loadLiked, loadPlaylists, loadRecent]);

  // Debounced search
  useEffect(() => {
    if (tab !== 'search' || !connected) return;
    const h = setTimeout(() => runSearch(query), 350);
    return () => clearTimeout(h);
  }, [query, tab, connected, runSearch]);

  // --- Handoff: pick a Spotify track → resolve YouTube → queue ---
  const handlePick = useCallback(async (track) => {
    setResolvingId(track.spotifyId);
    setError(null);
    try {
      const feat = audioFeatures[track.spotifyId];
      const enrichedTrack = { ...track, bpm: feat?.bpm, key: feat?.key, energy: feat?.energy };
      const resolved = await SpotifyClient.resolveToYouTube(enrichedTrack);
      if (!resolved?.best) {
        setError(`Couldn't find a YouTube match for "${track.title}"`);
        return;
      }
      // Show alternatives sheet — user confirms best OR picks different
      setHandoffRow({ track: enrichedTrack, ...resolved });
    } catch (e) {
      setError(e.message);
    } finally {
      setResolvingId(null);
    }
  }, [audioFeatures]);

  const confirmHandoff = useCallback((ytResult) => {
    if (!handoffRow) return;
    onAddToQueue({
      url: ytResult.url,
      ytId: ytResult.ytId,
      title: handoffRow.track.title,
      artist: handoffRow.track.artist,
      thumb: handoffRow.track.thumb || ytResult.thumb,
      duration: handoffRow.track.duration,
      bpm: handoffRow.track.bpm,
      key: handoffRow.track.key,
      source: 'spotify',
      spotifyId: handoffRow.track.spotifyId,
      ytTitle: ytResult.title, // keep so user can see what actually plays
    });
    setHandoffRow(null);
  }, [handoffRow, onAddToQueue]);

  // --- Render ---
  if (!connected) {
    return (
      <div className="panel spotify-panel" data-screen-label="Spotify">
        <div className="sp-head">
          <div className="sp-title">
            <span className="sp-logo"></span>
            STREAM<span className="sp-title-slash"> // </span><span className="sp-title-sub">SPCTRM</span>
          </div>
          {onClose && <button className="sp-close" onClick={onClose} title="Close">×</button>}
        </div>
        <div className="sp-connect-card">
          <div className="sp-connect-glyph"></div>
          <div className="sp-connect-title">Link a stream source</div>
          <div className="sp-connect-sub">
            Browse your library &amp; playlists. Tracks resolve to YouTube audio so you keep full DJ control — tempo, pitch, EQ, FX.
          </div>
          <button className="sp-connect-btn" onClick={() => SpotifyClient.beginLogin()}>
            <span className="sp-logo"></span> Connect
          </button>
          <div className="sp-connect-via">via <b>Spotify</b> · read-only</div>
          <div className="sp-connect-note">
            No playback data leaves this page.
          </div>
        </div>
      </div>
    );
  }

  const tabs = [
    { id: 'search', label: 'Search' },
    { id: 'liked', label: 'Liked' },
    { id: 'playlists', label: 'Playlists' },
    { id: 'recent', label: 'Recent' },
  ];

  return (
    <div className="panel spotify-panel" data-screen-label="Spotify">
      <div className="sp-head">
        <div className="sp-title">
          <span className="sp-logo"></span>
          STREAM<span className="sp-title-slash"> // </span><span className="sp-title-sub">SPCTRM</span>
        </div>
        <div className="sp-user">
          {me?.images?.[0]?.url && <img src={me.images[0].url} alt="" className="sp-avatar" />}
          <span className="sp-user-name">{me?.display_name || 'Connected'}</span>
          <button className="sp-logout" onClick={() => { SpotifyClient.logout(); setConnected(false); }} title="Disconnect">×</button>
        </div>
      </div>

      <div className="sp-tabs">
        {tabs.map(t => (
          <button
            key={t.id}
            className={`sp-tab ${tab === t.id ? 'active' : ''}`}
            onClick={() => { setTab(t.id); setOpenPlaylist(null); }}
          >{t.label}</button>
        ))}
      </div>

      {tab === 'search' && (
        <div className="sp-search-box">
          <input
            className="sp-search-input"
            placeholder="Search tracks, artists…"
            value={query}
            onChange={e => setQuery(e.target.value)}
            autoFocus
          />
        </div>
      )}

      {/* Tempo lane / sort toolbar — visible on track lists */}
      {tab !== 'playlists' || openPlaylist ? (
        <div className="sp-toolbar">
          <span className="sp-toolbar-label">Sort</span>
          <button
            className={`sp-sort-btn ${sortBy === 'default' ? 'active' : ''}`}
            onClick={() => setSortBy('default')}
          >Default</button>
          <button
            className={`sp-sort-btn ${sortBy === 'bpm' ? 'active' : ''}`}
            onClick={() => setSortBy('bpm')}
          >BPM</button>
          <button
            className={`sp-sort-btn ${sortBy === 'key' ? 'active' : ''}`}
            onClick={() => setSortBy('key')}
          >Key</button>
        </div>
      ) : null}

      {error && <div className="sp-error">{error}</div>}

      <div className="sp-list">
        {tab === 'search' && (
          <SPTrackList
            tracks={searchResults}
            loading={loading}
            audioFeatures={audioFeatures}
            onPick={handlePick}
            resolvingId={resolvingId}
            sortBy={sortBy}
            emptyLabel={query ? 'No results' : 'Start typing to search…'}
          />
        )}

        {tab === 'liked' && (
          <SPTrackList
            tracks={liked?.items || []}
            loading={loading && !liked}
            audioFeatures={audioFeatures}
            onPick={handlePick}
            resolvingId={resolvingId}
            sortBy={sortBy}
            emptyLabel="No liked songs yet"
          />
        )}

        {tab === 'playlists' && !openPlaylist && (
          <SPPlaylistGrid
            playlists={playlists?.items || []}
            loading={loading && !playlists}
            onOpen={openPlaylistDetail}
          />
        )}

        {tab === 'playlists' && openPlaylist && (
          <div>
            <button className="sp-back" onClick={() => setOpenPlaylist(null)}>← Playlists</button>
            <div className="sp-playlist-header">
              {openPlaylist.thumb && <img src={openPlaylist.thumb} alt="" className="sp-pl-thumb" />}
              <div>
                <div className="sp-pl-name">{openPlaylist.name}</div>
                <div className="sp-pl-meta">{openPlaylist.trackCount} tracks · {openPlaylist.owner}</div>
              </div>
            </div>
            <SPTrackList
              tracks={openPlaylist.tracks || []}
              loading={loading && !openPlaylist.tracks}
              audioFeatures={audioFeatures}
              onPick={handlePick}
              resolvingId={resolvingId}
              sortBy={sortBy}
              emptyLabel="Empty playlist"
            />
          </div>
        )}

        {tab === 'recent' && (
          <SPTrackList
            tracks={recent?.items || []}
            loading={loading && !recent}
            audioFeatures={audioFeatures}
            onPick={handlePick}
            resolvingId={resolvingId}
            sortBy={sortBy}
            emptyLabel="Play something on Spotify to see it here"
          />
        )}
      </div>

      {handoffRow && (
        <SPHandoffSheet
          data={handoffRow}
          onConfirm={confirmHandoff}
          onCancel={() => setHandoffRow(null)}
        />
      )}
    </div>
  );
}

// ============ Track list ============
const KEY_ORDER = ['1A','1B','2A','2B','3A','3B','4A','4B','5A','5B','6A','6B','7A','7B','8A','8B','9A','9B','10A','10B','11A','11B','12A','12B'];
function sortTracks(tracks, sortBy, audioFeatures) {
  if (sortBy === 'default' || !tracks?.length) return tracks;
  const out = [...tracks];
  if (sortBy === 'bpm') {
    out.sort((a, b) => {
      const ba = audioFeatures[a.spotifyId]?.bpm || 0;
      const bb = audioFeatures[b.spotifyId]?.bpm || 0;
      return ba - bb;
    });
  } else if (sortBy === 'key') {
    out.sort((a, b) => {
      const ka = audioFeatures[a.spotifyId]?.key;
      const kb = audioFeatures[b.spotifyId]?.key;
      const ia = ka ? KEY_ORDER.indexOf(ka) : 999;
      const ib = kb ? KEY_ORDER.indexOf(kb) : 999;
      return ia - ib;
    });
  }
  return out;
}

function SPTrackList({ tracks, loading, audioFeatures, onPick, resolvingId, emptyLabel, sortBy = 'default' }) {
  tracks = sortTracks(tracks, sortBy, audioFeatures);
  if (loading) {
    return (
      <div className="sp-loading">
        <div className="sp-skel" /><div className="sp-skel" /><div className="sp-skel" /><div className="sp-skel" />
      </div>
    );
  }
  if (!tracks.length) return <div className="sp-empty">{emptyLabel}</div>;
  return (
    <div className="sp-track-list">
      {tracks.map(t => {
        const feat = audioFeatures[t.spotifyId];
        const resolving = resolvingId === t.spotifyId;
        return (
          <div key={t.spotifyId} className="sp-track-row">
            <div className="sp-track-thumb">
              {t.thumb ? <img src={t.thumb} alt="" /> : <div className="sp-track-thumb-ph" />}
            </div>
            <div className="sp-track-info">
              <div className="sp-track-title" title={t.title}>{t.title}</div>
              <div className="sp-track-meta">
                <span className="sp-track-artist">{t.artist}</span>
                {feat?.bpm && <span className="sp-chip bpm">{feat.bpm} BPM</span>}
                {feat?.key && <span className="sp-chip key">{feat.key}</span>}
                {t.explicit && <span className="sp-chip explicit">E</span>}
                <span className="sp-track-dur">{fmtDur(t.duration)}</span>
              </div>
            </div>
            <button
              className="sp-track-add"
              onClick={() => onPick(t)}
              disabled={resolving}
              title="Queue this track"
            >
              {resolving ? '…' : '+'}
            </button>
          </div>
        );
      })}
    </div>
  );
}

// ============ Playlist grid ============
function SPPlaylistGrid({ playlists, loading, onOpen }) {
  if (loading) {
    return (
      <div className="sp-pl-grid">
        {[0,1,2,3].map(i => <div key={i} className="sp-pl-card skel" />)}
      </div>
    );
  }
  if (!playlists.length) return <div className="sp-empty">No playlists</div>;
  return (
    <div className="sp-pl-grid">
      {playlists.map(p => (
        <button key={p.id} className="sp-pl-card" onClick={() => onOpen(p)}>
          {p.thumb ? <img src={p.thumb} alt="" /> : <div className="sp-pl-card-ph" />}
          <div className="sp-pl-card-name" title={p.name}>{p.name}</div>
          <div className="sp-pl-card-meta">{p.trackCount} tracks</div>
        </button>
      ))}
    </div>
  );
}

// ============ Handoff confirmation sheet ============
function SPHandoffSheet({ data, onConfirm, onCancel }) {
  const { track, best, alternatives } = data;
  const [selected, setSelected] = useState(best);
  return (
    <div className="sp-handoff-overlay" onClick={onCancel}>
      <div className="sp-handoff" onClick={e => e.stopPropagation()}>
        <div className="sp-handoff-head">
          <div className="sp-handoff-title">Queue from YouTube</div>
          <button className="sp-close" onClick={onCancel}>×</button>
        </div>
        <div className="sp-handoff-track">
          {track.thumb && <img src={track.thumb} alt="" className="sp-handoff-thumb" />}
          <div>
            <div className="sp-handoff-name">{track.title}</div>
            <div className="sp-handoff-artist">{track.artist}</div>
          </div>
        </div>
        <div className="sp-handoff-sub">We found these YouTube matches:</div>
        <div className="sp-handoff-list">
          {[best, ...alternatives].filter(Boolean).map((c, i) => (
            <button
              key={c.ytId}
              className={`sp-yt-option ${selected?.ytId === c.ytId ? 'selected' : ''}`}
              onClick={() => setSelected(c)}
            >
              {c.thumb && <img src={c.thumb} alt="" className="sp-yt-thumb" />}
              <div className="sp-yt-info">
                <div className="sp-yt-title">{c.title}</div>
                <div className="sp-yt-meta">
                  {c.channel}
                  {c.duration && <> · {fmtDur(c.duration)}</>}
                  {i === 0 && <span className="sp-chip best">Best match</span>}
                </div>
              </div>
            </button>
          ))}
        </div>
        <div className="sp-handoff-actions">
          <button className="sp-btn secondary" onClick={onCancel}>Cancel</button>
          <button className="sp-btn primary" onClick={() => onConfirm(selected)} disabled={!selected}>
            Add to queue
          </button>
        </div>
      </div>
    </div>
  );
}

function fmtDur(s) {
  if (!s) return '';
  const m = Math.floor(s / 60);
  const sec = Math.floor(s % 60);
  return `${m}:${sec.toString().padStart(2, '0')}`;
}

window.SpotifyPanel = SpotifyPanel;
