import React, { useState, useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";
import {
  Hexagon,
  Home,
  Compass,
  Layers,
  Search,
  ChevronLeft,
  Play,
  Pause,
  SkipBack,
  SkipForward,
  Repeat,
  Volume2,
  Mic2,
  X,
  Check,
  Music2,
  ArrowUpRight,
  Download,
  Upload,
  ArrowRight,
} from "lucide-react";
import { ShareButton } from "./components/ShareButton";
import { songIdFromPath } from "./services/sharing";
import { BrandMark } from "./components/BrandMark";
import { CreatorCredit } from "./components/CreatorCredit";
import { playlists } from "./data/mock";
import { read, save, putSong, getSongs } from "./services/storage";
import { exportPublication } from "./services/publications";
import { loadCatalog } from "./services/catalog";
import { refreshReleaseMedia } from "./services/remotePublications";
import { AdminPanel } from "./components/AdminPanel";
import { PageTitle } from "./components/Collection";
import { UploadPage, Editor } from "./components/Studio";
import { DiscoveryHero } from "./components/DiscoveryHero";
import { LyricsExperience } from "./components/LyricsExperience";
import "./styles.css";
import "./redesign.css";
import "./publisher.css";
import "./identity.css";
const time = (n) =>
  `${Math.floor((n || 0) / 60)}:${String(Math.floor((n || 0) % 60)).padStart(2, "0")}`;
const hasAudio = (song) => Boolean(song?.audio || song?.audioUrl);
const currentPath = () =>
  "/" + location.pathname.split("/").filter(Boolean).join("/");
const emptyLyrics = { type: "line-sync", language: "en", lines: [] };

function App() {
  // This switch opens a local authoring workspace, not an authenticated admin area.
  const studio = new URLSearchParams(location.search).get("studio") === "1";
  const [route, setRoute] = useState(currentPath());
  const [page, setPage] = useState(
    currentPath() === "/galaxy"
      ? "Lyrics"
      : currentPath() === "/admin"
        ? "Admin"
        : songIdFromPath(currentPath())
          ? "Song"
          : studio
            ? "Studio"
            : "Home",
  );
  useEffect(() => {
    const pop = () => {
      setRoute(currentPath());
      setPage(
        currentPath() === "/galaxy"
          ? "Lyrics"
          : currentPath() === "/admin"
            ? "Admin"
            : songIdFromPath(currentPath())
              ? "Song"
              : "Home",
      );
    };
    window.addEventListener("popstate", pop);
    return () => window.removeEventListener("popstate", pop);
  }, []);
  const [published, setPublished] = useState([]),
    [drafts, setDrafts] = useState([]);
  const [current, setCurrent] = useState(null),
    [detailDraft, setDetail] = useState(null);
  const [playing, setPlaying] = useState(false),
    [position, setPosition] = useState(0),
    [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(read("volume", 0.75)),
    [speed, setSpeed] = useState(read("speed", 1)),
    [repeat, setRepeat] = useState(false);
  const [query, setQuery] = useState(""),
    [filter, setFilter] = useState("All"),
    [toast, setToast] = useState("");
  const [loading, setLoading] = useState(true),
    [loadError, setLoadError] = useState(""),
    [attempt, setAttempt] = useState(0),
    [exporting, setExporting] = useState(false);
  const draftUrls = useRef([]);
  useEffect(() => () => draftUrls.current.forEach(URL.revokeObjectURL), []);
  const audio = useRef(null),
    searchRef = useRef(null),
    pendingPlay = useRef(false);
  useEffect(() => {
    if (page === "Admin") audio.current?.pause();
  }, [page]);
  const catalog = studio ? [...published, ...drafts] : published;
  const routeSongId = songIdFromPath(route);
  const detail = routeSongId
    ? catalog.find((song) => song.id === routeSongId) || null
    : detailDraft;
  const notify = (message) => setToast(message);
  function nav(target) {
    if (route !== "/") {
      history.pushState(null, "", "/");
      setRoute("/");
    }
    setPage(target);
    setQuery("");
    setDetail(null);
    window.scrollTo({
      top: 0,
      behavior: matchMedia("(prefers-reduced-motion: reduce)").matches
        ? "instant"
        : "smooth",
    });
  }
  useEffect(() => {
    const controller = new AbortController();
    let disposed = false;
    const urls = [];
    setLoading(true);
    setLoadError("");
    loadCatalog(controller.signal)
      .then((songs) => {
        if (disposed) return;
        const hydrated = songs.map((s) => {
          if (!s.cover) return s;
          const art = URL.createObjectURL(s.cover);
          urls.push(art);
          return { ...s, art };
        });
        setPublished(hydrated);
        setCurrent(
          (previous) =>
            hydrated.find((s) => s.id === songIdFromPath(currentPath())) ||
            hydrated.find((s) => s.id === previous?.id) ||
            hydrated.find(
              (s) =>
                s.id === new URLSearchParams(location.search).get("release"),
            ) ||
            hydrated.find((s) => s.id === read("current", "")) ||
            hydrated[0] ||
            null,
        );
      })
      .catch((error) => {
        if (!disposed) setLoadError(error.message);
      })
      .finally(() => {
        if (!disposed) setLoading(false);
      });
    return () => {
      disposed = true;
      controller.abort();
      urls.forEach(URL.revokeObjectURL);
    };
  }, [attempt]);
  useEffect(() => {
    if (!studio) return;
    let disposed = false;
    const urls = [];
    getSongs()
      .then((songs) => {
        if (disposed) return;
        setDrafts(
          songs.map((s) => {
            if (!s.cover) return s;
            const art = URL.createObjectURL(s.cover);
            urls.push(art);
            return { ...s, art };
          }),
        );
      })
      .catch(() => notify("Local drafts could not be restored."));
    return () => {
      disposed = true;
      urls.forEach(URL.revokeObjectURL);
    };
  }, [studio]);
  useEffect(() => {
    if (!toast) return;
    const timer = setTimeout(() => setToast(""), 5000);
    return () => clearTimeout(timer);
  }, [toast]);
  useEffect(() => {
    const a = audio.current;
    setPlaying(false);
    setPosition(0);
    setDuration(0);
    if (!hasAudio(current)) {
      a.removeAttribute("src");
      a.load();
      return;
    }
    const url = current.audio
      ? URL.createObjectURL(current.audio)
      : current.audioUrl;
    a.src = url;
    a.load();
    if (pendingPlay.current) {
      pendingPlay.current = false;
      a.play().catch(() => notify("Press play to begin listening."));
    }
    return () => {
      a.pause();
      a.removeAttribute("src");
      a.load();
      if (current.audio) URL.revokeObjectURL(url);
    };
  }, [current?.id, current?.audio, current?.audioUrl]);
  useEffect(() => {
    audio.current.volume = volume;
    save("volume", volume);
  }, [volume]);
  useEffect(() => {
    audio.current.playbackRate = speed;
    save("speed", speed);
  }, [speed, current]);
  async function togglePlay() {
    if (!audio.current.paused) {
      audio.current.pause();
      return;
    }
    if (
      current?.remote &&
      !current.audio &&
      Date.now() - current.signedAt > 50 * 60 * 1000
    ) {
      try {
        pendingPlay.current = true;
        setCurrent(await refreshReleaseMedia(current));
      } catch (error) {
        notify(error.message);
      }
      return;
    }
    if (!hasAudio(current)) {
      notify(
        "This release contains lyrics only. Audio has not been published.",
      );
      return;
    }
    if (audio.current.paused)
      audio.current
        .play()
        .catch(() => notify("This audio format could not be played."));
    else audio.current.pause();
  }
  async function choose(song, autoplay = false) {
    if (
      song.remote &&
      !song.audio &&
      Date.now() - song.signedAt > 50 * 60 * 1000
    ) {
      try {
        song = await refreshReleaseMedia(song);
      } catch (error) {
        notify(error.message);
        return;
      }
    }
    if (current?.id === song.id) {
      if (autoplay) togglePlay();
      return;
    }
    pendingPlay.current = !!(autoplay && hasAudio(song));
    setCurrent(song);
    save("current", song.id);
    if (autoplay && !hasAudio(song)) notify("This is a lyrics-only release.");
  }
  function next(offset = 1) {
    const playable = catalog.filter(hasAudio);
    if (!playable.length) return;
    const index = playable.findIndex((s) => s.id === current?.id);
    choose(
      playable[(index + offset + playable.length) % playable.length],
      true,
    );
  }
  function seek(value) {
    if (!hasAudio(current) || !Number.isFinite(audio.current.duration)) return;
    audio.current.currentTime = Math.max(
      0,
      Math.min(value, audio.current.duration),
    );
    setPosition(audio.current.currentTime);
  }
  useEffect(() => {
    function handle(e) {
      if ((e.metaKey || e.ctrlKey) && e.key === "k") {
        e.preventDefault();
        searchRef.current?.focus();
        return;
      }
      if (
        ["INPUT", "TEXTAREA", "SELECT", "BUTTON"].includes(e.target.tagName) ||
        e.target.isContentEditable
      )
        return;
      if (e.code === "Space") {
        e.preventDefault();
        togglePlay();
      }
      if (e.code === "ArrowRight") seek(audio.current.currentTime + 5);
      if (e.code === "ArrowLeft") seek(audio.current.currentTime - 5);
    }
    window.addEventListener("keydown", handle);
    return () => window.removeEventListener("keydown", handle);
  }, [current]);
  async function saveDraft(song) {
    await putSong(song);
    const art = song.cover ? URL.createObjectURL(song.cover) : "/artwork/1.svg";
    if (song.cover) draftUrls.current.push(art);
    setDrafts((list) => [...list, { ...song, art }]);
    choose({ ...song, art });
    nav("Studio");
    notify(
      "Draft saved on this device. Export the publication files to release it on the site.",
    );
  }
  async function exportSite() {
    setExporting(true);
    try {
      await exportPublication(drafts, published);
      notify(
        "Downloaded catalog.json and release packages. Copy all files to public/releases and deploy the site.",
      );
    } catch (error) {
      notify(error.message);
    } finally {
      setExporting(false);
    }
  }
  const IconButton = ({ icon: Icon, label, onClick, active, disabled }) => (
    <button
      className={`icon-button ${active ? "active" : ""}`}
      title={label}
      aria-label={label}
      aria-pressed={active}
      onClick={onClick}
      disabled={disabled}
    >
      <Icon size={19} />
    </button>
  );
  function openSong(song) {
    setDetail(song);
    const path = `/song/${encodeURIComponent(song.id)}`;
    history.pushState(null, "", path);
    setRoute(path);
    setPage("Song");
    setQuery("");
  }
  function SongCard({ song }) {
    return (
      <article className="song-card">
        <button
          className="cover-button"
          onClick={() => openSong(song)}
          aria-label={`View ${song.title}`}
        >
          <img src={song.art} alt={`${song.title} artwork`} />
          <span className="cover-play">
            <ArrowUpRight size={25} />
          </span>
          <span className="lyric-badge">
            {hasAudio(song) ? "AUDIO + LYRICS" : "LYRICS EDITION"}
          </span>
        </button>
        <div className="song-card-info">
          <button onClick={() => openSong(song)}>
            <strong>{song.title}</strong>
            <span>{song.artist}</span>
          </button>
          <ShareButton song={song} compact />
          <button
            className="release-enter"
            aria-label={`Read ${song.title}`}
            onClick={() => {
              choose(song);
              nav("Lyrics");
            }}
          >
            <Mic2 size={18} />
          </button>
        </div>
      </article>
    );
  }
  function SectionHead({ title, subtitle, action, onClick }) {
    return (
      <div className="section-heading">
        <div>
          <h2>{title}</h2>
          {subtitle && <p>{subtitle}</p>}
        </div>
        {action && (
          <button className="text-link" onClick={onClick}>
            {action}
            <ArrowRight size={15} />
          </button>
        )}
      </div>
    );
  }
  function ReleaseRows({ items }) {
    return (
      <div className="song-rows">
        {items.map((song, i) => (
          <div className="song-row" key={song.id}>
            <span className="row-number">{String(i + 1).padStart(2, "0")}</span>
            <button
              className="row-art"
              onClick={() => {
                choose(song);
                nav("Lyrics");
              }}
              aria-label={`Open ${song.title}`}
            >
              <img src={song.art} alt="" />
              <Play size={16} />
            </button>
            <button className="row-name" onClick={() => openSong(song)}>
              <strong>{song.title}</strong>
              <span>{song.artist}</span>
            </button>
            <span className="row-album">{song.album}</span>
            <span className="tag">
              {hasAudio(song) ? "AUDIO + LYRICS" : "LYRICS EDITION"}
            </span>
            <button
              className="text-link"
              onClick={() => {
                choose(song);
                nav("Lyrics");
              }}
            >
              Enter <ArrowUpRight size={15} />
            </button>
          </div>
        ))}
      </div>
    );
  }
  const filtered = catalog.filter(
    (s) =>
      (filter === "All" || s.genre === filter) &&
      `${s.title} ${s.artist} ${s.genre}`
        .toLowerCase()
        .includes(query.toLowerCase()),
  );
  return (
    <div className="app publisher-app">
      <audio
        ref={audio}
        onPlay={() => setPlaying(true)}
        onPause={() => setPlaying(false)}
        onTimeUpdate={() => setPosition(audio.current.currentTime)}
        onLoadedMetadata={() => {
          setDuration(audio.current.duration);
          audio.current.playbackRate = speed;
        }}
        onError={() => {
          setPlaying(false);
          notify("The published audio could not be decoded.");
        }}
        onEnded={() => {
          if (repeat) {
            audio.current.currentTime = 0;
            audio.current.play().catch(() => {});
          } else next();
        }}
      />
      <aside className="sidebar">
        <button className="brand" onClick={() => nav("Home")}>
          <span className="brand-mark">
            <BrandMark size={38} />
          </span>
          <span>
            hexora<span className="brand-sub">MELODIA</span>
          </span>
        </button>
        <span className="nav-label">AN INDEPENDENT PUBLICATION</span>
        <nav>
          {[
            [Home, "Home"],
            [Compass, "Releases"],
            [Layers, "Selections"],
            [Mic2, "Lyrics"],
          ].map(([Icon, target]) => (
            <button
              key={target}
              onClick={() => {
                setFilter("All");
                nav(target);
              }}
              className={page === target ? "selected" : ""}
            >
              <Icon size={19} />
              {target}
            </button>
          ))}
        </nav>
        <div className="publisher-sidebar-note">
          <span className="eyebrow">SOUND. WORDS. ATMOSPHERE.</span>
          <p>
            A small, considered universe of music.
            <br />
            Selected and published by Hexora.
          </p>
          <div className="publisher-edition">
            INDEPENDENT BY DESIGN <span>✦</span>
          </div>
        </div>
        {studio && (
          <nav className="studio-nav">
            <button
              onClick={() => nav("Studio")}
              className={page === "Studio" ? "selected" : ""}
            >
              <Upload size={18} />
              Publisher studio
            </button>
            <span className="studio-local-label">
              LOCAL AUTHORING WORKSPACE
            </span>
          </nav>
        )}
      </aside>
      <div className="main-shell">
        <header className="topbar">
          <div className="breadcrumbs">
            <button aria-label="Back to home" onClick={() => nav("Home")}>
              <ChevronLeft size={20} />
            </button>
            <span>
              {page === "Home" ? "The independent music journal" : page}
            </span>
          </div>
          <label className="search">
            <Search size={17} />
            <input
              ref={searchRef}
              value={query}
              onChange={(e) => {
                setFilter("All");
                setQuery(e.target.value);
              }}
              placeholder="Search the releases…"
              aria-label="Search music"
            />
            <kbd>⌘ K</kbd>
          </label>
        </header>
        <main key={page} className="page-enter">
          {studio && (
            <div className="studio-notice">
              LOCAL STUDIO · Drafts stay on this device. Public releases change
              only when you export files and deploy the site.
            </div>
          )}
          {loading && page !== "Admin" ? (
            <div className="publication-loading" role="status">
              Opening the archive…
            </div>
          ) : loadError && page !== "Admin" ? (
            <div className="empty">
              <h2>The archive is temporarily unavailable.</h2>
              <p role="alert">{loadError}</p>
              <button
                className="primary"
                onClick={() => setAttempt((n) => n + 1)}
              >
                Try again
              </button>
            </div>
          ) : query ? (
            <>
              <SectionHead title={`Results for “${query}”`} />
              <div className="album-grid">
                {filtered.map((s) => (
                  <SongCard song={s} key={s.id} />
                ))}
              </div>
              {!filtered.length && (
                <p>No releases found. Try a title or artist.</p>
              )}
            </>
          ) : (
            <>
              {page === "Admin" && (
                <AdminPanel
                  onPreview={(song) => {
                    if (song.cover) {
                      song = { ...song, art: URL.createObjectURL(song.cover) };
                      draftUrls.current.push(song.art);
                    } else song = { ...song, art: "/artwork/1.svg" };
                    setCurrent(song);
                    nav("Lyrics");
                  }}
                  onPublished={() => setAttempt((n) => n + 1)}
                />
              )}
              {page === "Home" && (
                <>
                  <DiscoveryHero
                    onListen={() => nav("Lyrics")}
                    onDiscover={() => nav("Releases")}
                  />
                  <SectionHead
                    title="The latest editions"
                    subtitle="A deliberate selection. A different way to listen."
                    action="All releases"
                    onClick={() => nav("Releases")}
                  />
                  <div className="album-grid publication-grid">
                    {published.map((s) => (
                      <SongCard song={s} key={s.id} />
                    ))}
                  </div>
                  <section className="publisher-manifesto">
                    <span className="eyebrow">FROM THE EDITOR</span>
                    <h2>
                      Less noise.
                      <br />
                      <em>More meaning.</em>
                    </h2>
                    <div>
                      <p>
                        Music deserves room to breathe. An independent archive
                        of releases, words and the worlds they open.
                      </p>
                      <button
                        className="text-link"
                        onClick={() => nav("Lyrics")}
                      >
                        Disappear into the listening room{" "}
                        <ArrowUpRight size={17} />
                      </button>
                    </div>
                    <span className="manifesto-star" aria-hidden="true">
                      ✳
                    </span>
                  </section>
                  <SectionHead
                    title="A universe in every line"
                    subtitle="Three ways to experience the words. One place to get lost."
                  />
                  <div className="experience-invitations">
                    {[
                      ["cinema", "01", "Cinema", "The words take the stage."],
                      [
                        "combo",
                        "02",
                        "Combo",
                        "The moment, and the whole story.",
                      ],
                      [
                        "galaxy",
                        "03",
                        "Galaxy",
                        "A little further from everything.",
                      ],
                    ].map(([mode, number, name, text]) => (
                      <button
                        key={mode}
                        onClick={() => {
                          save("lyricsView", mode);
                          nav("Lyrics");
                          if (mode === "galaxy") {
                            history.pushState(
                              null,
                              "",
                              `/galaxy?release=${encodeURIComponent(current?.id || "")}`,
                            );
                            setRoute("/galaxy");
                          }
                        }}
                      >
                        <small>{number} / LISTENING EXPERIENCE</small>
                        <h3>
                          {name}
                          <ArrowUpRight size={22} />
                        </h3>
                        <p>{text}</p>
                      </button>
                    ))}
                  </div>
                </>
              )}
              {page === "Releases" && (
                <>
                  <PageTitle
                    eyebrow="THE PUBLISHED ARCHIVE"
                    title="Sound, carefully selected."
                    text="Every release, published by Hexora. Explore the audio, lyrics and translations."
                  />
                  <div className="filter-bar">
                    {[
                      "All",
                      ...new Set(catalog.map((s) => s.genre).filter(Boolean)),
                    ].map((g) => (
                      <button
                        key={g}
                        className={filter === g ? "chosen" : ""}
                        onClick={() => setFilter(g)}
                      >
                        {g}
                      </button>
                    ))}
                  </div>
                  <div className="album-grid publication-grid">
                    {filtered.map((s) => (
                      <SongCard song={s} key={s.id} />
                    ))}
                  </div>
                </>
              )}
              {page === "Selections" && (
                <>
                  <PageTitle
                    eyebrow="EDITORIAL SELECTIONS"
                    title="A mood. A moment. A world."
                    text="Listening notes and collections from the publisher."
                  />
                  {playlists.map((p, i) => (
                    <section className="editorial-selection" key={p.name}>
                      <div>
                        <span className="eyebrow">{p.tag}</span>
                        <h2>{p.name}</h2>
                        <p>{p.description}</p>
                      </div>
                      <ReleaseRows
                        items={published.filter(
                          (_, j) => j % playlists.length === i,
                        )}
                      />
                    </section>
                  ))}
                </>
              )}
              {page === "Lyrics" &&
                (current ? (
                  <LyricsExperience
                    key={current.id}
                    song={current}
                    lyrics={current.lyrics || emptyLyrics}
                    translations={current.translations || {}}
                    playing={playing}
                    position={position}
                    duration={duration}
                    onPlay={togglePlay}
                    onSeek={seek}
                    onBack={() => nav("Home")}
                    initialMode={route === "/galaxy" ? "galaxy" : undefined}
                    onModeChange={(mode) => {
                      if (mode === "galaxy") {
                        history.pushState(
                          null,
                          "",
                          `/galaxy?release=${encodeURIComponent(current.id)}`,
                        );
                        setRoute("/galaxy");
                      } else if (route === "/galaxy") {
                        history.pushState(null, "", "/");
                        setRoute("/");
                      }
                    }}
                    onEdit={studio ? () => nav("Editor") : undefined}
                  />
                ) : (
                  <p>No release has been published yet.</p>
                ))}
              {page === "Song" && !detail && (
                <div className="release-unavailable">
                  <h1>This release isn’t available.</h1>
                  <p>It may have been unpublished, or the link is incorrect.</p>
                  <button className="primary" onClick={() => nav("Home")}>
                    Browse the archive
                  </button>
                </div>
              )}
              {page === "Song" && detail && (
                <>
                  <div className="song-detail">
                    <img src={detail.art} alt={`${detail.title} artwork`} />
                    <div>
                      <span className="eyebrow">
                        {detail.genre} / {detail.year} / PUBLISHED EDITION
                      </span>
                      <h1>{detail.title}</h1>
                      <p>
                        {detail.artist} / {detail.album}
                      </p>
                      <div className="release-detail-actions">
                        <ShareButton song={detail} />

                        {hasAudio(detail) && (
                          <button
                            className="primary"
                            onClick={() => choose(detail, true)}
                          >
                            <Play size={17} />
                            Listen
                          </button>
                        )}
                        <button
                          className="secondary"
                          onClick={() => {
                            choose(detail);
                            nav("Lyrics");
                          }}
                        >
                          <Mic2 size={17} />
                          Enter the listening room
                        </button>
                        {detail.packageUrl && (
                          <a
                            className="secondary download-release"
                            href={detail.packageUrl}
                            download
                          >
                            <Download size={16} />
                            Download LYRA
                          </a>
                        )}
                      </div>
                      <p className="demo-note">
                        {hasAudio(detail)
                          ? "Published audio with synchronized lyrics"
                          : "Lyrics and translation edition · no audio included"}
                      </p>
                    </div>
                  </div>
                  <SectionHead title="Inside the words" />
                  {(detail.lyrics?.lines || []).slice(0, 3).map((l) => (
                    <p className="detail-lyric" dir="auto" key={l.id}>
                      {l.text}
                    </p>
                  ))}
                  <SectionHead title="More from the archive" />
                  <ReleaseRows
                    items={published.filter((s) => s.id !== detail.id)}
                  />
                </>
              )}
              {studio && page === "Studio" && (
                <>
                  <PageTitle
                    eyebrow="PUBLISHER WORKSPACE"
                    title="Prepare the next edition."
                    text="Author locally, export release files, then deploy. This workspace does not upload to a server."
                  />
                  <div className="studio-toolbar">
                    <button className="primary" onClick={() => nav("Upload")}>
                      <Upload size={17} />
                      New release draft
                    </button>
                    <button
                      className="secondary"
                      disabled={!drafts.length || exporting}
                      onClick={exportSite}
                    >
                      <Download size={17} />
                      {exporting ? "Preparing…" : "Export publication files"}
                    </button>
                  </div>
                  <div className="panel">
                    <h2>Publish to the site</h2>
                    <p>
                      Export downloads catalog.json and one LYRA package per
                      draft. Allow multiple downloads if your browser asks.
                      Place every downloaded file in public/releases, replacing
                      catalog.json, then build and deploy. Until then, visitors
                      see only the existing published archive. This is a local
                      editing tool, not an authenticated admin service.
                    </p>
                  </div>
                  <SectionHead title="Local drafts" />
                  {drafts.length ? (
                    <ReleaseRows items={drafts} />
                  ) : (
                    <p>No local drafts yet.</p>
                  )}
                  <SectionHead title="Published archive" />
                  <ReleaseRows items={published} />
                </>
              )}
              {studio && page === "Upload" && (
                <UploadPage onPublish={saveDraft} notify={notify} />
              )}
              {studio && page === "Editor" && current && (
                <Editor
                  key={current.id}
                  song={current}
                  audio={audio}
                  position={position}
                  playing={playing}
                  togglePlay={togglePlay}
                  seek={seek}
                  notify={notify}
                  onSave={async (updated) => {
                    if (current.published) {
                      notify(
                        "Create a local release draft before editing. Published packages remain unchanged.",
                      );
                      return;
                    }
                    try {
                      const song = { ...current, ...updated };
                      await putSong(song);
                      setCurrent(song);
                      setDrafts((list) =>
                        list.map((s) => (s.id === song.id ? song : s)),
                      );
                      notify(
                        "Draft lyrics saved. Export publication files to publish.",
                      );
                    } catch {
                      notify("Draft could not be saved to browser storage.");
                    }
                  }}
                />
              )}
            </>
          )}
          <CreatorCredit />
          <footer className="page-footer">
            <span>
              <BrandMark size={26} /> hexora melodia
            </span>
            <p>An independent music publication.</p>
            <span>
              SOUND / WORDS / ATMOSPHERE <span className="purple">✦</span>
            </span>
            <a className="publisher-access" href="/admin">
              Publisher access
            </a>
          </footer>
        </main>
      </div>
      {current && (
        <div className="player">
          <div className="now-playing">
            <button onClick={() => nav("Lyrics")}>
              <img src={current.art} alt="" />
            </button>
            <button className="track-info" onClick={() => nav("Lyrics")}>
              <strong>{current.title}</strong>
              <span>
                {current.artist} /{" "}
                {hasAudio(current) ? "PUBLISHED AUDIO" : "LYRICS EDITION"}
              </span>
            </button>
          </div>
          <div className="player-center">
            <div className="transport">
              <IconButton
                icon={SkipBack}
                label="Previous release"
                onClick={() => next(-1)}
              />
              <button
                className="play-button"
                aria-label={playing ? "Pause" : "Play"}
                onClick={togglePlay}
                disabled={!hasAudio(current)}
              >
                {playing ? (
                  <Pause size={20} fill="currentColor" />
                ) : (
                  <Play size={20} fill="currentColor" />
                )}
              </button>
              <IconButton
                icon={SkipForward}
                label="Next release"
                onClick={() => next()}
              />
              <IconButton
                icon={Repeat}
                label="Repeat release"
                active={repeat}
                onClick={() => setRepeat(!repeat)}
              />
            </div>
            <div className="seek">
              <span>{time(position)}</span>
              <input
                aria-label="Seek playback"
                type="range"
                min="0"
                max={duration || 1}
                step=".1"
                value={position}
                disabled={!hasAudio(current)}
                onChange={(e) => seek(+e.target.value)}
                style={{
                  "--progress": `${duration ? (position / duration) * 100 : 0}%`,
                }}
              />
              <span>{duration ? time(duration) : "–:––"}</span>
            </div>
          </div>
          <div className="player-tools">
            <select
              aria-label="Playback speed"
              value={speed}
              onChange={(e) => setSpeed(+e.target.value)}
            >
              {[0.5, 0.75, 1, 1.25, 1.5, 2].map((s) => (
                <option value={s} key={s}>
                  {s}×
                </option>
              ))}
            </select>
            <IconButton
              icon={Mic2}
              label="Synchronized lyrics"
              active={page === "Lyrics"}
              onClick={() => nav("Lyrics")}
            />
            <IconButton
              icon={Volume2}
              label="Mute or restore volume"
              onClick={() => setVolume(volume ? 0 : 0.75)}
            />
            <input
              className="volume"
              aria-label="Volume"
              type="range"
              min="0"
              max="1"
              step=".01"
              value={volume}
              onChange={(e) => setVolume(+e.target.value)}
            />
          </div>
        </div>
      )}
      {toast && (
        <div className="toast" role="status">
          <Check size={17} />
          {toast}
          <button
            aria-label="Dismiss notification"
            onClick={() => setToast("")}
          >
            <X size={15} />
          </button>
        </div>
      )}
    </div>
  );
}
createRoot(document.getElementById("root")).render(<App />);
