/* global React, FileIcon, FolderIcon */
/* ============================================================================
   sidebar.jsx — the left navigation column.
   ----------------------------------------------------------------------------
   Two jobs:
     1. NAVIGATION — a file-tree of every section. Clicking a "file" shows that
        section in the main column; clicking a project name jumps to the
        projects page and scrolls to that project.
     2. LIVE WIDGETS — a small panel with Melbourne local time, current weather,
        and a background-music toggle.

   On desktop it's a sticky column; on mobile (≤880px, see styles.css) it
   collapses into a top bar with a hamburger that opens/closes the tree.

   Props (passed down from App in app.jsx):
     data      — window.PORTFOLIO
     active    — id of the section currently shown on the home page
     setActive — switch the home-page section
     page      — "home" | "projects"
     navigate  — switch between the home and projects pages (with transition)
   ============================================================================ */

// The navigation tree, grouped exactly as it appears in the sidebar. Each
// `id` matches a section component in app.jsx's SECTIONS map. To add or rename
// an entry, edit this list (and the matching section file).
const NAV_GROUPS = [
  {
    label: "~ portfolio",
    items: [
      { id: "readme", label: "README.md",     icon: "file" },
      { id: "about",  label: "about.md",       icon: "file" },
      { id: "now",    label: "now.md",         icon: "file" },
      { id: "skills", label: "skills.md",      icon: "file" },
      { id: "exp",    label: "experience.md",  icon: "file" },
    ],
  },
  {
    label: "~ misc",
    items: [
      { id: "gallery", label: "gallery/",     icon: "folder" },
      { id: "travel",  label: "travel.md",    icon: "file" },
      { id: "edu",     label: "education.md",  icon: "file" },
      { id: "contact", label: "contact.md",   icon: "file" },
    ],
  },
];

function Sidebar({ data, active, setActive, page, navigate }) {
  const p = data.person;
  const [mobileOpen, setMobileOpen] = React.useState(false);
  const close = () => setMobileOpen(false);

  // Show a home-page section. If we're on the projects page, hop home first.
  const selectFile = (id) => {
    setActive(id);
    if (page !== "home") navigate("home");
    close();
  };

  // Jump to one project on the projects page and scroll to its card.
  const jumpToProject = (id) => {
    close();
    const scroll = () => {
      const el = document.getElementById(`prj-${id}`);
      if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 24, behavior: "smooth" });
    };
    // Already on projects → scroll now; otherwise navigate then wait for the wash.
    if (page === "projects") setTimeout(scroll, 50);
    else { navigate("projects"); setTimeout(scroll, 750); }
  };

  return (
    <aside className="rm-side" data-open={mobileOpen ? "1" : "0"}>
      {/* Identity + mobile hamburger */}
      <div className="rm-id">
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div className="rm-av">{p.alias[0]}</div>
          <div className="who">
            <b>{p.name}</b><br />
            <span>{p.handle}</span>
          </div>
        </div>
        <button className="rm-menu-btn" type="button"
                aria-label={mobileOpen ? "Close menu" : "Open menu"} aria-expanded={mobileOpen}
                onClick={() => setMobileOpen((v) => !v)}>
          {mobileOpen ? (
            <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M3 3 L13 13 M13 3 L3 13" /></svg>
          ) : (
            <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M2 4h12 M2 8h12 M2 12h12" /></svg>
          )}
        </button>
      </div>

      {/* Everything below collapses on mobile */}
      <div className="rm-side-content">
        <div className="rm-tree">
          {/* First group: the main portfolio sections */}
          <NavGroup group={NAV_GROUPS[0]} page={page} active={active} onSelect={selectFile} />

          {/* Projects: the folder opens the projects page; the nested list is
              the real project NAMES (not slugs) and jumps to each card. */}
          <div className="group">~ projects</div>
          <button data-on={page === "projects" ? "1" : "0"} data-cursor-label="open"
                  onClick={() => { navigate("projects"); close(); }}>
            <FolderIcon on={page === "projects"} /><span>projects/</span>
          </button>
          <div className="nested">
            {data.projects.map((pr) => (
              <button key={pr.id} data-cursor-label="jump" onClick={() => jumpToProject(pr.id)}>
                <FileIcon /><span>{pr.name}</span>
              </button>
            ))}
          </div>

          {/* Last group: gallery / travel / education / contact */}
          <NavGroup group={NAV_GROUPS[1]} page={page} active={active} onSelect={selectFile} />
        </div>

        {/* Quick status block */}
        <div className="rm-side-meta">
          <div className="row"><span>status</span><b><span style={{ color: "var(--accent)" }}>●</span> open</b></div>
          <div className="row"><span>based</span><b>{p.location}</b></div>
          <div className="row"><span>tz</span><b>{p.timezone}</b></div>
          <div className="row"><span>updated</span><b>May 2026</b></div>
        </div>

        <LiveWidgets />
      </div>
    </aside>
  );
}

// One labelled group of file links.
function NavGroup({ group, page, active, onSelect }) {
  return (
    <>
      <div className="group">{group.label}</div>
      {group.items.map((it) => {
        // A file is "on" only when we're on the home page AND it's the active one.
        const on = page === "home" && active === it.id;
        return (
          <button key={it.id} data-on={on ? "1" : "0"} data-cursor-label={it.label}
                  onClick={() => onSelect(it.id)}>
            {it.icon === "folder" ? <FolderIcon on={on} /> : <FileIcon on={on} />}
            <span>{it.label}</span>
          </button>
        );
      })}
    </>
  );
}

/* ── Live widgets: clock + weather + music ──────────────────────────────────── */

// Map an Open-Meteo WMO weather code to a small icon + label.
function wmoFlavor(code) {
  if (code == null) return { icon: "·", label: "—" };
  if (code === 0)  return { icon: "☀",  label: "clear"   };
  if (code <= 3)   return { icon: "⛅", label: "cloudy"  };
  if (code <= 48)  return { icon: "🌫", label: "fog"     };
  if (code <= 67)  return { icon: "🌧", label: "rain"    };
  if (code <= 77)  return { icon: "❄",  label: "snow"    };
  if (code <= 82)  return { icon: "🌦", label: "showers" };
  if (code <= 99)  return { icon: "⛈", label: "thunder" };
  return { icon: "·", label: "—" };
}

// Melbourne time, formatted with Intl in the Australia/Melbourne timezone.
function formatMelbourneTime(date) {
  try {
    const time = new Intl.DateTimeFormat("en-AU", { timeZone: "Australia/Melbourne", hour: "numeric", minute: "2-digit", hour12: true }).format(date);
    const day  = new Intl.DateTimeFormat("en-AU", { timeZone: "Australia/Melbourne", weekday: "short" }).format(date).toLowerCase();
    return { time, day };
  } catch (e) {
    return { time: date.toLocaleTimeString(), day: "" };
  }
}

function LiveWidgets() {
  // Live local time, ticking every 30s.
  const [time, setTime] = React.useState(() => formatMelbourneTime(new Date()));
  React.useEffect(() => {
    const tick = () => setTime(formatMelbourneTime(new Date()));
    tick();
    const id = setInterval(tick, 30 * 1000);
    return () => clearInterval(id);
  }, []);

  // Current weather from Open-Meteo (no API key, CORS-friendly).
  const [weather, setWeather] = React.useState(null);
  const [wxError, setWxError] = React.useState(false);
  React.useEffect(() => {
    let cancelled = false;
    fetch("https://api.open-meteo.com/v1/forecast?latitude=-37.81&longitude=144.96&current_weather=true&timezone=Australia%2FMelbourne")
      .then((r) => r.json())
      .then((d) => { if (!cancelled) setWeather(d.current_weather || null); })
      .catch(() => { if (!cancelled) setWxError(true); });
    return () => { cancelled = true; };
  }, []);
  const flavor = wmoFlavor(weather?.weathercode);

  return (
    <div className="rm-extras">
      <div className="city">Melbourne · AEST</div>
      <div className="time">{time.time}<small>{time.day}</small></div>
      <div className={"weather" + (weather ? "" : " loading")}>
        <span className="ic">{flavor.icon}</span>
        <span className="t">{weather ? `${Math.round(weather.temperature)}°C` : (wxError ? "—" : "…")}</span>
        <span className="l">{weather ? flavor.label : (wxError ? "offline" : "loading")}</span>
      </div>
      <MusicPlayer title="Hope Is the Thing With Feathers" artist="HOYO-MiX, Robin, Chevy" volume={0.10} />
    </div>
  );
}

// Background-music toggle. Drives the shared <audio id="bg-music"> from
// index.html. State persists in localStorage, and native audio events keep the
// play/pause icon in sync with reality. Browsers need a user gesture before the
// first play, so a stored "on" state tries to resume and silently stays paused
// if the browser blocks it.
function MusicPlayer({ title, artist, audioId = "bg-music", volume = 0.08 }) {
  const [playing, setPlaying] = React.useState(false);
  const [error, setError]     = React.useState(false);
  const intentRef = React.useRef(false); // whether the user *wants* it playing

  React.useEffect(() => {
    const a = document.getElementById(audioId);
    if (!a) { setError(true); return; }
    a.volume = volume;
    a.loop = true;

    const onPlay  = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    const onErr   = () => setError(true);
    const onEnded = () => { if (intentRef.current) { try { a.currentTime = 0; a.play().catch(() => {}); } catch (e) {} } };
    a.addEventListener("play", onPlay);
    a.addEventListener("pause", onPause);
    a.addEventListener("error", onErr);
    a.addEventListener("ended", onEnded);

    // Resume a previous session if it was left on.
    let wasOn = false;
    try { wasOn = localStorage.getItem("rm-music") === "1"; } catch (e) {}
    if (wasOn) { intentRef.current = true; a.play().catch(() => {}); }

    return () => {
      a.removeEventListener("play", onPlay);
      a.removeEventListener("pause", onPause);
      a.removeEventListener("error", onErr);
      a.removeEventListener("ended", onEnded);
    };
  }, [audioId, volume]);

  const toggle = () => {
    const a = document.getElementById(audioId);
    if (!a) return;
    if (playing) {
      intentRef.current = false;
      a.pause();
      try { localStorage.setItem("rm-music", "0"); } catch (e) {}
    } else {
      a.play()
        .then(() => { intentRef.current = true; setError(false); try { localStorage.setItem("rm-music", "1"); } catch (e) {} })
        .catch(() => setError(true));
    }
  };

  return (
    <div className="rm-music">
      <div className="head">
        <span className="lbl">{playing ? "now playing" : "music"}</span>
        <button className="ctrl" onClick={toggle} aria-label={playing ? "Pause" : "Play"} title={playing ? "Pause" : "Play"}>
          {playing ? "❚❚" : "▶"}
        </button>
      </div>
      <div className="track">
        <div className="bars" data-on={playing ? "1" : "0"}><i /><i /><i /></div>
        <div className="meta">
          <div className="t">{title}</div>
          <div className="a">{artist}</div>
        </div>
      </div>
      {error && (
        <div className="err">
          Couldn't load audio. Make sure <code>public/background.mp3</code> exists.
        </div>
      )}
    </div>
  );
}

Object.assign(window, { Sidebar });
