/* global React, ReactDOM, TopNav, MobileBottomNav, Player, LoginScreen, HomeScreen, SongScreen, PlaylistScreen, SearchScreen, ArtistScreen, AccountScreen, AdminScreen, MobilePage */
/* global TweaksPanel, TweakSection, TweakRadio, TweakColor, TweakToggle, useTweaks */
/* global CatalogStore */

const { useState: useA, useEffect: useEA } = React;

const DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "oklch(0.56 0.22 25)",
  "view": "App",
  "page": "Home",
  "grain": "subtle",
  "fog": true,
  "playing": false
}/*EDITMODE-END*/;

const DEFAULT_STREAMING_TRACKS = [
  {
    id: "get-low",
    title: "Get Low (Nu-Metal Version)",
    shortTitle: "Get Low",
    artist: "Viral Tension",
    album: "Pressure",
    duration: 34,
    variant: "mic",
    audioUrl: "media/get-low-demo.wav",
  },
  {
    id: "walk-down",
    title: "Walk Down",
    shortTitle: "Walk Down",
    artist: "Viral Tension",
    album: "Pressure",
    duration: 36,
    variant: "stage",
    audioUrl: "media/walk-down-demo.wav",
  },
  {
    id: "iron-lungs",
    title: "Iron Lungs",
    shortTitle: "Iron Lungs",
    artist: "Viral Tension",
    album: "Pressure",
    duration: 38,
    variant: "iron",
    audioUrl: "media/iron-lungs-demo.wav",
  },
];

const REMOTE_CATALOG_URL = "https://pub-43ea72da2a94444bac472c3ede7c77b9.r2.dev/catalog/songs.json";
const POPULAR_TRACK_ORDER = [
  "lil-jon-and-the-eastside-boyz-get-low",
  "50-cent-in-da-club",
  "dmx-ruff-ryders-anthem",
  "eminem-without-me",
  "eminem-till-i-collapse",
  "dmx-x-gon-give-it-to-ya",
  "jay-z-99-problems",
  "coolio-gangsta-s-paradise",
  "outkast-hey-ya",
  "ice-cube-it-was-a-good-day",
  "the-notorious-b-i-g-hypnotize",
  "dr-dre-the-next-episode",
  "cypress-hill-insane-in-the-brain",
  "house-of-pain-jump-around",
  "wu-tang-clan-protect-ya-neck",
  "metallica-nothing-else-matters",
  "foo-fighters-everlong",
  "kid-rock-only-god-knows-why",
  "johnny-cash-hurt",
  "morgan-wallen-whiskey-glasses",
  "travis-scott-sicko-mode",
  "post-malone-i-fall-apart",
  "kanye-west-stronger",
  "2pac-so-many-tears",
  "lil-jon-snap-yo-fingers",
];

const TRACK_STATS = {
  "lil-jon-and-the-eastside-boyz-get-low": {
    plays: 554179,
  },
  "50-cent-in-da-club": {
    plays: 152717,
  },
  "eminem-till-i-collapse": {
    plays: 147539,
  },
  "afroman-colt-45": {
    plays: 79277,
  },
  "dmx-x-gon-give-it-to-ya": {
    plays: 76926,
  },
  "ludacris-get-back": {
    plays: 69055,
  },
  "coolio-gangsta-s-paradise": {
    plays: 47658,
  },
  "cypress-hill-rock-superstar": {
    plays: 43239,
  },
  "eminem-stan": {
    plays: 41260,
  },
  "roy-jones-jr-can-t-be-touched": {
    plays: 37714,
  },
  "eminem-the-real-slim-shady": {
    plays: 36378,
  },
  "t-i-bring-em-out": {
    plays: 35954,
  },
  "dmx-ruff-ryders-anthem": {
    plays: 33642,
  },
  "fort-minor-remember-the-name": {
    plays: 33225,
  },
  "dr-dre-and-ice-cube-natural-born-killaz": {
    plays: 27738,
  },
  "viral-tension-built-from-it": {
    plays: 28503,
  },
  "eminem-without-me": {
    plays: 23227,
  },
  "eminem-xzibit-nate-dogg-say-my-name": {
    plays: 23000,
  },
  "house-of-pain-jump-around": {
    plays: 22326,
  },
  "dr-dre-keep-their-heads-ringin": {
    plays: 20780,
  },
};

const EXPLICIT_TRACK_IDS = new Set([
  "lil-jon-and-the-eastside-boyz-get-low",
  "50-cent-in-da-club",
  "afroman-colt-45",
  "dmx-x-gon-give-it-to-ya",
  "ludacris-get-back",
  "coolio-gangsta-s-paradise",
  "cypress-hill-rock-superstar",
  "eminem-stan",
  "roy-jones-jr-can-t-be-touched",
  "eminem-the-real-slim-shady",
  "t-i-bring-em-out",
  "dmx-ruff-ryders-anthem",
  "dr-dre-and-ice-cube-natural-born-killaz",
  "eminem-without-me",
  "eminem-xzibit-nate-dogg-say-my-name",
  "dr-dre-the-next-episode",
  "house-of-pain-who-s-the-man",
  "ludacris-get-back",
]);

const enrichTrack = (track) => ({
  ...track,
  ...(TRACK_STATS[track.id] || {}),
  explicit: track.explicit === true || (track.explicit !== false && EXPLICIT_TRACK_IDS.has(track.id)),
});

const sortByPopularity = (items) => {
  const rank = new Map(POPULAR_TRACK_ORDER.map((id, index) => [id, index]));
  return items.map(enrichTrack).sort((a, b) => {
    if ((b.plays || 0) !== (a.plays || 0)) return (b.plays || 0) - (a.plays || 0);
    const ar = rank.has(a.id) ? rank.get(a.id) : 1000;
    const br = rank.has(b.id) ? rank.get(b.id) : 1000;
    if (ar !== br) return ar - br;
    return (a.title || "").localeCompare(b.title || "");
  });
};

const loadJson = (url) => new Promise((resolve) => {
  const req = new XMLHttpRequest();
  req.open("GET", url, true);
  req.onload = () => {
    if (req.status >= 200 && req.status < 300) {
      try {
        resolve(JSON.parse(req.responseText));
      } catch (error) {
        resolve([]);
      }
    } else {
      resolve([]);
    }
  };
  req.onerror = () => resolve([]);
  req.send();
});

const App = () => {
  const [t, setT] = useTweaks(DEFAULTS);
  const [authed, setAuthed] = useA(false);
  const urlParams = new URLSearchParams(window.location.search);
  const ownerMode = urlParams.get("admin") === "1" || window.location.hash === "#admin";
  const showTweaks = urlParams.get("tweaks") === "1";
  const [page, setPage] = useA(ownerMode ? "Admin" : "Home");
  const [accountMode, setAccountMode] = useA("premium"); // preview · trial · premium
  const [showExplicitContent, setShowExplicitContent] = useA(true);
  const [selectedPlaylistId, setSelectedPlaylistId] = useA(null);
  const [savedTrackIds, setSavedTrackIds] = useA(() => new Set());
  const [tracks, setTracks] = useA(DEFAULT_STREAMING_TRACKS);
  const [currentTrack, setCurrentTrack] = useA(DEFAULT_STREAMING_TRACKS[0]);
  const [playQueue, setPlayQueue] = useA([]);

  const hydrateSong = (song) => ({
    ...song,
    audioUrl: song.audioBlob ? URL.createObjectURL(song.audioBlob) : song.audioUrl,
    artUrl: song.artBlob ? URL.createObjectURL(song.artBlob) : song.artUrl,
  });

  useEA(() => {
    let alive = true;
    Promise.all([
      Promise.resolve(window.R2_STREAMING_TRACKS || []).then((songs) => songs.length ? songs : loadJson(REMOTE_CATALOG_URL)),
      CatalogStore.listSongs().catch(() => []),
    ]).then(([remoteSongs, savedSongs]) => {
      if (!alive) return;
      const localSongs = savedSongs.map(hydrateSong);
      const nextTracks = sortByPopularity(remoteSongs.length ? [...remoteSongs, ...localSongs] : [...DEFAULT_STREAMING_TRACKS, ...localSongs]);
      setTracks(nextTracks);
      setCurrentTrack(nextTracks[0] || DEFAULT_STREAMING_TRACKS[0]);
    }).catch(() => {});
    return () => { alive = false; };
  }, []);

  // sync tweak.page <-> internal page
  useEA(() => { if (t.page && t.page !== page) setPage(t.page); }, [t.page]);
  useEA(() => { if (ownerMode) setPage("Admin"); }, []);
  useEA(() => { if (page !== t.page) setT("page", page); }, [page]);

  // apply accent to CSS variable
  useEA(() => {
    document.documentElement.style.setProperty("--accent", t.accent);
    // derive variants
    document.documentElement.style.setProperty("--accent-soft", t.accent.replace(")", " / .18)").replace("oklch(", "oklch("));
    document.documentElement.style.setProperty("--accent-glow", t.accent.replace(")", " / .55)").replace("oklch(", "oklch("));
  }, [t.accent]);

  const renderPage = () => {
    const openPlaylist = (playlistId = "popular") => {
      setSelectedPlaylistId(playlistId);
      setPage("Playlists");
    };
    switch (page) {
      case "Home":      return <HomeScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} onOpenPlaylist={openPlaylist} savedTrackIds={savedTrackIds} />;
      case "Browse":    return <SearchScreen onNav={setPage} tracks={tracks} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} />;
      case "Playlists": return <PlaylistScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} selectedPlaylistId={selectedPlaylistId} setSelectedPlaylistId={setSelectedPlaylistId} savedTrackIds={savedTrackIds} toggleSavedTrack={toggleSavedTrack} />;
      case "Library":   return <PlaylistScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} selectedPlaylistId={selectedPlaylistId} setSelectedPlaylistId={setSelectedPlaylistId} savedTrackIds={savedTrackIds} toggleSavedTrack={toggleSavedTrack} />;
      case "Search":    return <SearchScreen onNav={setPage} tracks={tracks} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} />;
      case "Song":      return <SongScreen onNav={setPage} track={currentTrack} tracks={tracks} onPlayTrack={playTrack} isPlaying={t.playing} setPlaying={(v) => setT("playing", v)} showExplicitContent={showExplicitContent} isLiked={savedTrackIds.has(currentTrack.id)} onToggleLike={() => toggleSavedTrack(currentTrack.id)} />;
      case "Playlist":  return <PlaylistScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} selectedPlaylistId={selectedPlaylistId} setSelectedPlaylistId={setSelectedPlaylistId} savedTrackIds={savedTrackIds} toggleSavedTrack={toggleSavedTrack} />;
      case "Artist":    return <ArtistScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} />;
      case "Account":   return <AccountScreen onNav={setPage} accountMode={accountMode} setAccountMode={setAccountMode} showExplicitContent={showExplicitContent} setShowExplicitContent={setShowExplicitContent} />;
      case "Admin":     return ownerMode ? <AdminScreen onNav={setPage} tracks={tracks} onAddTrack={addUploadedTrack} onPlayTrack={playTrack} /> : <HomeScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} onOpenPlaylist={openPlaylist} savedTrackIds={savedTrackIds} />;
      case "Mobile":    return <MobilePage />;
      default:          return <HomeScreen onNav={setPage} tracks={tracks} currentTrack={currentTrack} onPlayTrack={playTrack} showExplicitContent={showExplicitContent} onOpenPlaylist={openPlaylist} savedTrackIds={savedTrackIds} />;
    }
  };

  const playTrack = (track, queueTracks = null) => {
    setCurrentTrack(track);
    if (Array.isArray(queueTracks) && queueTracks.length) setPlayQueue(queueTracks);
    setT("playing", true);
    setPage("Song");
  };

  const moveInQueue = (direction) => {
    const queue = playQueue.length ? playQueue : tracks;
    if (!queue.length) return;
    const currentIndex = Math.max(0, queue.findIndex((item) => item.id === currentTrack.id));
    const nextIndex = (currentIndex + direction + queue.length) % queue.length;
    setCurrentTrack(queue[nextIndex]);
    setT("playing", true);
  };

  const toggleSavedTrack = (trackId) => {
    setSavedTrackIds((current) => {
      const next = new Set(current);
      if (next.has(trackId)) next.delete(trackId);
      else next.add(trackId);
      return next;
    });
  };

  const addUploadedTrack = async (draft) => {
    const saved = await CatalogStore.addSong({
      ...draft,
      shortTitle: draft.title,
      artist: draft.artist || "Viral Tension",
      album: draft.album || "Singles",
      variant: "stage",
    });
    const hydrated = hydrateSong(saved);
    setTracks((items) => [...items, hydrated]);
    setCurrentTrack(hydrated);
    setT("playing", false);
    return hydrated;
  };

  // Login view: stand-alone
  if (t.view === "Login") {
    return (
      <>
        <LoginScreen onEnter={() => { setAuthed(true); setAccountMode("preview"); setT("view", "App"); }} />
        {showTweaks && <TweakControls t={t} setT={setT} />}
      </>
    );
  }

  if (t.view === "Mobile") {
    return (
      <div className="app">
        {t.grain !== "off" && <div className={`grain ${t.grain === "strong" ? "strong" : ""}`}></div>}
        <TopNav active={"Home"} onNav={(p) => setPage(p)} accentDot currentTrack={currentTrack} />
        <MobilePage />
        {showTweaks && <TweakControls t={t} setT={setT} />}
      </div>
    );
  }

  if (!authed && t.view === "Auth") {
    return (
      <>
        <LoginScreen onEnter={() => { setAuthed(true); setAccountMode("preview"); setT("view", "App"); }} />
        {showTweaks && <TweakControls t={t} setT={setT} />}
      </>
    );
  }

  return (
    <div className="app">
      {t.grain !== "off" && <div className={`grain ${t.grain === "strong" ? "strong" : ""}`}></div>}
      <TopNav active={page === "Song" || page === "Playlist" ? "Home" : page} onNav={(p) => setPage(p)} accentDot accountMode={accountMode} currentTrack={currentTrack} />
      {accountMode === "preview" && <PreviewGate onUpgrade={() => setAccountMode("trial")} />}
      {renderPage()}
      <MobileBottomNav active={page === "Song" || page === "Playlist" ? "Home" : page} onNav={(p) => setPage(p)} />
      {t.playing && (
        <Player
          track={{ ...currentTrack, access: accountMode === "preview" ? "30 sec preview" : accountMode === "trial" ? "3-day trial" : "Premium" }}
          isLiked={savedTrackIds.has(currentTrack.id)}
          onToggleLike={() => toggleSavedTrack(currentTrack.id)}
          onNextTrack={() => moveInQueue(1)}
          onPrevTrack={() => moveInQueue(-1)}
          isPlaying={t.playing}
          setPlaying={(v) => setT("playing", v)}
          previewSeconds={accountMode === "preview" ? 30 : null}
        />
      )}
      {showTweaks && <TweakControls t={t} setT={setT} />}
    </div>
  );
};

const PreviewGate = ({ onUpgrade }) => (
  <div className="preview-gate glass">
    <div>
      <b>Free preview mode</b>
      <span>Every track plays a 30-second preview. Start a 3-day trial to unlock full Viral Tension streaming.</span>
    </div>
    <button className="btn btn-primary" onClick={onUpgrade}>Start 3-Day Trial · $4.99/mo</button>
  </div>
);

const TweakControls = ({ t, setT }) => (
  <TweaksPanel title="Tweaks · Viral Tension">
    <TweakSection label="View">
      <TweakRadio
        value={t.view}
        onChange={(v) => setT("view", v)}
        options={[
          { value: "App", label: "App" },
          { value: "Login", label: "Login" },
          { value: "Mobile", label: "Mobile" },
        ]}
      />
    </TweakSection>

    <TweakSection label="Page">
      <TweakRadio
        value={t.page}
        onChange={(v) => setT("page", v)}
        options={[
          { value: "Home", label: "Home" },
          { value: "Song", label: "Song" },
          { value: "Playlist", label: "Playlist" },
          { value: "Search", label: "Search" },
          { value: "Artist", label: "Artist" },
          { value: "Admin", label: "Admin" },
          { value: "Mobile", label: "Mobile" },
        ]}
      />
    </TweakSection>

    <TweakSection label="Accent">
      <TweakColor
        value={t.accent}
        onChange={(v) => setT("accent", v)}
        options={[
          "oklch(0.56 0.22 25)",   // crimson (default)
          "oklch(0.58 0.20 12)",   // blood
          "oklch(0.62 0.16 50)",   // ember orange
          "oklch(0.60 0.17 240)",  // steel blue
          "oklch(0.78 0.03 240)",  // ghost white
        ]}
      />
    </TweakSection>

    <TweakSection label="Grain">
      <TweakRadio
        value={t.grain}
        onChange={(v) => setT("grain", v)}
        options={[
          { value: "off", label: "Off" },
          { value: "subtle", label: "Subtle" },
          { value: "strong", label: "Strong" },
        ]}
      />
    </TweakSection>

    <TweakSection label="Playback">
      <TweakToggle
        value={t.playing}
        onChange={(v) => setT("playing", v)}
        label="Currently playing"
      />
    </TweakSection>
  </TweaksPanel>
);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
