// Visual building blocks — Waveform, TrackCard, GenreSection, Hero, About, Header, Footer.

// ── Procedural waveform ────────────────────────────────────────────────────
function mulberry32(seed) {
  return function () {
    let t = (seed += 0x6d2b79f5);
    t = Math.imul(t ^ (t >>> 15), t | 1);
    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

function makeWavePeaks(seed, n = 110) {
  const rnd = mulberry32(seed);
  const peaks = [];
  let trend = 0.5;
  for (let i = 0; i < n; i++) {
    trend += (rnd() - 0.5) * 0.18;
    trend = Math.max(0.08, Math.min(0.96, trend));
    const edge = Math.min(i, n - 1 - i) / (n * 0.16);
    const env = Math.min(1, edge);
    const jitter = (rnd() - 0.5) * 0.22;
    peaks.push(Math.max(0.06, Math.min(1, (trend + jitter) * env)));
  }
  return peaks;
}

const CONTACT_EMAIL = "syyoonmusic@gmail.com";

function CopyGlyph({ copied }) {
  return (
    <svg width="17" height="17" viewBox="0 0 17 17" aria-hidden="true">
      <rect
        x="5.5"
        y="3.5"
        width="8"
        height="10"
        rx="1.5"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.2"
      />
      <rect
        x="3.5"
        y="5.5"
        width="8"
        height="8"
        rx="1.5"
        fill={copied ? "currentColor" : "none"}
        fillOpacity={copied ? "0.12" : "0"}
        stroke="currentColor"
        strokeWidth="1.2"
      />
    </svg>
  );
}

function Waveform({ seed, progress = 0, onSeek, height = 56, accent }) {
  const peaks = React.useMemo(() => makeWavePeaks(seed, 120), [seed]);
  const ref = React.useRef(null);
  const dragging = React.useRef(false);

  const seekFrom = (clientX) => {
    if (!ref.current || !onSeek) return;
    const r = ref.current.getBoundingClientRect();
    const p = Math.max(0, Math.min(1, (clientX - r.left) / r.width));
    onSeek(p);
  };

  const onDown = (e) => {
    dragging.current = true;
    seekFrom(e.clientX);
    const move = (ev) => dragging.current && seekFrom(ev.clientX);
    const up = () => {
      dragging.current = false;
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  };

  const W = 1000;
  const H = 100;
  const mid = H / 2;
  const barW = W / peaks.length;
  const gap = barW * 0.42;
  const bw = barW - gap;

  return (
    <div
      ref={ref}
      onPointerDown={onDown}
      style={{
        position: "relative",
        height,
        cursor: onSeek ? "pointer" : "default",
        userSelect: "none",
        touchAction: "none",
      }}
    >
      <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none"
           style={{ width: "100%", height: "100%", display: "block" }}>
        <defs>
          <clipPath id={`clip-${seed}`}>
            <rect x="0" y="0" width={W * progress} height={H} />
          </clipPath>
        </defs>
        <g opacity="0.28">
          {peaks.map((p, i) => {
            const h = p * (H - 8);
            return (
              <rect key={i} x={i * barW + gap / 2} y={mid - h / 2}
                    width={bw} height={h} rx={bw / 2} fill="currentColor" />
            );
          })}
        </g>
        <g clipPath={`url(#clip-${seed})`}>
          {peaks.map((p, i) => {
            const h = p * (H - 8);
            return (
              <rect key={i} x={i * barW + gap / 2} y={mid - h / 2}
                    width={bw} height={h} rx={bw / 2}
                    fill={accent || "currentColor"} />
            );
          })}
        </g>
      </svg>
      <div style={{
        position: "absolute", top: -4, bottom: -4,
        left: `${progress * 100}%`,
        width: 1,
        background: accent || "currentColor",
        opacity: progress > 0 ? 0.9 : 0,
        pointerEvents: "none",
        transform: "translateX(-50%)",
      }} />
    </div>
  );
}

// ── Cover art (vinyl-label hybrid) ─────────────────────────────────────────

function CoverArt({ src, title, size = 96 }) {
  if (!src) return null;
  return (
    <img
      className="cover-art"
      src={src}
      alt={`${title} album art`}
      loading="lazy"
      decoding="async"
      style={{
        width: size,
        height: size,
        display: "block",
        objectFit: "cover",
        borderRadius: 1,
      }}
    />
  );
}

// ── Portfolio header (i18n-aware) ──────────────────────────────────────────

function PortfolioHead() {
  const { t } = useI18n();
  const p = t.portfolio;
  const total = CATALOG.reduce((n, g) => n + g.tracks.length, 0);
  return (
    <header className="portfolio-head">
      <div className="portfolio-label">{p.label}</div>
      <h2 className="portfolio-title">{p.title}</h2>
      <p className="portfolio-blurb">{p.blurb}</p>
      <div className="portfolio-meta">
        <span>{total} {p.tracks}</span>
        <span>·</span>
        <span>{p.stems}</span>
      </div>
    </header>
  );
}

// ── Track meta (i18n-aware) ────────────────────────────────────────────────

function TrackMeta({ track }) {
  const { t } = useI18n();
  const tm = t.track;
  const game = (t.games && t.games[track.gameKey]) || track.game || "";
  return (
    <div className="track-meta">
      <span><em>{tm.game}</em>{game}</span>
      <span><em>{tm.year}</em>{track.year}</span>
    </div>
  );
}

// ── Track card ─────────────────────────────────────────────────────────────

function PlayGlyph({ playing, size = 14 }) {
  if (playing) {
    return (
      <svg width={size} height={size} viewBox="0 0 14 14">
        <rect x="3" y="2" width="3" height="10" fill="currentColor" />
        <rect x="8" y="2" width="3" height="10" fill="currentColor" />
      </svg>
    );
  }
  return (
    <svg width={size} height={size} viewBox="0 0 14 14">
      <path d="M3 2 L12 7 L3 12 Z" fill="currentColor" />
    </svg>
  );
}

function TrackCard({ track, genre, accent }) {
  const audio = useAudioState();
  const isCurrent = audio.trackId === track.id;
  const isPlaying = isCurrent && audio.isPlaying;
  const hasCoverArt = Boolean(track.artSrc);
  const progress = isCurrent && audio.duration > 0
    ? audio.currentTime / audio.duration
    : 0;
  const elapsed = isCurrent ? formatTime(audio.currentTime) : "0:00";

  const onSeek = (p) => {
    if (!isCurrent) return;
    AudioEngine.seek(p * audio.duration);
  };

  return (
    <article
      className={`track ${isCurrent ? "is-current" : ""}`}
      data-screen-label={`Track ${genre.code}·${track.no}`}
    >
      <div className={`track-row ${hasCoverArt ? "" : "no-cover-art"}`}>
        <CoverArt src={track.artSrc} title={track.title} size={96} />

        <button
          type="button"
          className="play-btn"
          aria-label={isPlaying ? "Pause" : "Play"}
          onClick={() => AudioEngine.playTrack(track)}
        >
          <PlayGlyph playing={isPlaying} size={13} />
        </button>

        <div className="track-main">
          <div className="track-head">
            <div className="track-no">{track.no}</div>
            <h3 className="track-title">{track.title}</h3>
            <div className="track-dur">{elapsed} / {track.duration}</div>
          </div>

          <div className="wave-wrap" style={{ color: "var(--ink)" }}>
            <Waveform
              seed={track.seed}
              progress={progress}
              onSeek={onSeek}
              accent={accent}
              height={44}
            />
          </div>

          <TrackMeta track={track} />
        </div>
      </div>
    </article>
  );
}

// ── Genre section ──────────────────────────────────────────────────────────

const ROMAN = { I: "I", II: "II", III: "III" };

function GenreSection({ genre, accent }) {
  return (
    <section
      className="genre"
      id={`genre-${genre.id}`}
      data-screen-label={`Genre ${genre.code} ${genre.title}`}
    >
      <header className="genre-head">
        <div className="genre-code-block">
          <div className="genre-code">Side · {genre.code}</div>
          <div className="genre-roman">{ROMAN[genre.code] || genre.code}</div>
        </div>
        <h2 className="genre-title">{genre.title}</h2>
        <p className="genre-blurb">{genre.blurb}</p>
        <div className="genre-meta">
          <span>{genre.tracks.length} tracks</span>
          <span>·</span>
          <span>Stems on request</span>
        </div>
      </header>

      <div className="track-list">
        {genre.tracks.map((t) => (
          <TrackCard key={t.id} track={t} genre={genre} accent={accent} />
        ))}
      </div>
    </section>
  );
}

// ── Hero ───────────────────────────────────────────────────────────────────

function Hero({ tweaks }) {
  const { t } = useI18n();
  const h = t.hero;
  return (
    <section className="hero" data-screen-label="Hero">
      <div className="hero-top">
        <div className="hero-top-l">
          <span>{h.catL}</span>
          <span>·</span>
          <span>{h.vol}</span>
        </div>
        <div className="hero-top-c">{h.center}</div>
        <div className="hero-top-r">{h.right}</div>
      </div>

      <div className="hero-title-block">
        <h1 className="hero-title">
          <span className="name-row">Siyun Yoon</span>
          <span className="alt"><span className="amp">—</span> {h.altLine.replace(/^—\s*/, "")}</span>
        </h1>
      </div>

      <div className="hero-bottom">
        <p className="hero-statement">
          {h.statement[0]}<em>{h.statement[1]}</em>{h.statement[2]}
        </p>
        <div className="hero-cta">
          <a className="hero-cta-link" href="#portfolio">
            {h.cta}
          </a>
          <span className="hero-cta-meta">
            {CATALOG.reduce((n, g) => n + g.tracks.length, 0)} {h.ctaMetaSuffix}
          </span>
        </div>
      </div>
    </section>
  );
}

// ── About ──────────────────────────────────────────────────────────────────

function About() {
  const { t } = useI18n();
  const a = t.about;
  return (
    <section className="about" id="about" data-screen-label="About">
      <div className="about-head">
        <div className="about-label">{a.label}</div>
        {a.labelR && <div className="about-label-r">{a.labelR}</div>}
      </div>
      <div className="about-grid">
        <div className="about-portrait">
          <div className="portrait-frame">
            <div className="portrait-placeholder">
              <img
                className="portrait-image"
                src="uploads/about-portrait.jpg"
                alt={a.portraitAlt}
                loading="lazy"
                decoding="async"
              />
              {a.portraitTag && <span className="portrait-tag">{a.portraitTag}</span>}
            </div>
          </div>
          {(a.portraitName || a.portraitSub) && (
            <div className="portrait-caption">
              {a.portraitName}
              {a.portraitSub && <span>{a.portraitSub}</span>}
            </div>
          )}
        </div>

        <div className="about-body">
          <p className="about-lede">
            {Array.isArray(a.lede) ? (
              <>
                {a.lede[0]}<em>{a.lede[1]}</em>{a.lede[2]}
              </>
            ) : (
              a.lede
            )}
          </p>
          {a.p1 && <p>{a.p1}</p>}
          {(a.p2a || a.p2b || a.p2c) && (
            <p>
              {a.p2a}<strong>{a.p2b}</strong>{a.p2c}
            </p>
          )}
        </div>
      </div>
    </section>
  );
}

// ── Header / nav ───────────────────────────────────────────────────────────

function SiteHeader() {
  const { t } = useI18n();
  return (
    <header className="site-header">
      <div className="brand">
        <div className="brand-meta">
          <div className="brand-name">Siyun Yoon</div>
          <div className="brand-role">Game Music</div>
        </div>
      </div>
      <nav className="site-nav">
        <a href="#portfolio">{t.nav.portfolio}</a>
        <a href="#about">{t.nav.about}</a>
        <a href="#contact">{t.nav.contact}</a>
      </nav>
    </header>
  );
}

// ── Footer ─────────────────────────────────────────────────────────────────

function SiteFooter() {
  const { t } = useI18n();
  const f = t.footer;
  const based = t.about.facts;
  const [copied, setCopied] = React.useState(false);
  const copiedTimerRef = React.useRef(null);

  React.useEffect(() => {
    return () => {
      if (copiedTimerRef.current) {
        window.clearTimeout(copiedTimerRef.current);
      }
    };
  }, []);

  const copyEmail = async () => {
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        await navigator.clipboard.writeText(CONTACT_EMAIL);
      } else {
        const textArea = document.createElement("textarea");
        textArea.value = CONTACT_EMAIL;
        textArea.setAttribute("readonly", "");
        textArea.style.position = "absolute";
        textArea.style.left = "-9999px";
        document.body.appendChild(textArea);
        textArea.select();
        document.execCommand("copy");
        document.body.removeChild(textArea);
      }

      setCopied(true);
      if (copiedTimerRef.current) {
        window.clearTimeout(copiedTimerRef.current);
      }
      copiedTimerRef.current = window.setTimeout(() => {
        setCopied(false);
      }, 1800);
    } catch (error) {
      console.error("Failed to copy email", error);
    }
  };

  return (
    <footer className="site-footer" id="contact" data-screen-label="Footer">
      <div className="foot-detail">
        <div className="foot-detail-label">{based.based}</div>
        <div className="foot-detail-value">{based.basedV}</div>
      </div>
      <div className="foot-eyebrow">{t.nav.contact}</div>
      <div className="foot-contact-row">
        <div className="foot-headline foot-headline-display">
          {CONTACT_EMAIL}
        </div>
        <button
          type="button"
          className={`foot-copy-btn ${copied ? "is-copied" : ""}`}
          onClick={copyEmail}
          aria-label={copied ? "Email copied" : "Copy email"}
        >
          <CopyGlyph copied={copied} />
          <span>{copied ? "Copied" : "Copy"}</span>
        </button>
      </div>
      <div className="foot-base">
        <span>SY · MMXXVI</span>
        <span>{f.pressed}</span>
        <span>{f.sides}</span>
      </div>
    </footer>
  );
}

Object.assign(window, {
  Waveform, CoverArt, TrackCard, TrackMeta, PortfolioHead, GenreSection,
  Hero, About, SiteHeader, SiteFooter,
});
