// Recorded KJV narration from the completed Tribe Truth Bible Audio Generator.
// One chapter MP3 and one timestamp manifest are served publicly from R2.

const BIBLE_AUDIO_BASE = "https://media.tribe-truth.com/bible-audio/kjv/bible-reader/v1";
const BIBLE_MANIFEST_CACHE = new Map();

function bibleAudioSlug(bookName) {
  return String(bookName || "")
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

function bibleChapterPath(bookName, chapter, file) {
  const slug = bibleAudioSlug(bookName);
  const chapterPart = String(Number(chapter) || 1).padStart(3, "0");
  return `${BIBLE_AUDIO_BASE}/${slug}/${chapterPart}/${file}`;
}

function formatBibleAudioTime(seconds) {
  if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
  const whole = Math.floor(seconds);
  return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
}

function BibleNarratedChapter({ bookName, chapter, verses, compact = false }) {
  const sitePlayer = usePlayer();
  const audioRef = useRef(null);
  const stopAtMsRef = useRef(null);
  const [manifest, setManifest] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [playing, setPlaying] = useState(false);
  const [positionMs, setPositionMs] = useState(0);
  const [durationMs, setDurationMs] = useState(0);

  const manifestUrl = bibleChapterPath(bookName, chapter, "manifest.json");

  useEffect(() => {
    let cancelled = false;
    const controller = new AbortController();
    const audio = audioRef.current;
    if (audio) {
      audio.pause();
      audio.currentTime = 0;
    }
    stopAtMsRef.current = null;
    setManifest(null);
    setLoading(true);
    setError("");
    setPlaying(false);
    setPositionMs(0);
    setDurationMs(0);

    const cached = BIBLE_MANIFEST_CACHE.get(manifestUrl);
    const request = cached
      ? Promise.resolve(cached)
      : fetch(manifestUrl, { signal: controller.signal })
          .then((response) => {
            if (!response.ok) throw new Error(`Narration unavailable (${response.status})`);
            return response.json();
          })
          .then((data) => {
            BIBLE_MANIFEST_CACHE.set(manifestUrl, data);
            return data;
          });

    request
      .then((data) => {
        if (!cancelled) {
          setManifest(data);
          setDurationMs(Number(data?.audio?.duration_ms) || 0);
          setLoading(false);
        }
      })
      .catch((reason) => {
        if (!cancelled && reason?.name !== "AbortError") {
          setError(reason?.message || "Narration could not be loaded.");
          setLoading(false);
        }
      });

    return () => {
      cancelled = true;
      controller.abort();
      if (audio) audio.pause();
    };
  }, [manifestUrl]);

  const activeVerse = manifest?.verses?.find(
    (verse) => positionMs >= Number(verse.start_ms) && positionMs < Number(verse.end_ms),
  ) || null;
  const activeWord = activeVerse?.words?.find(
    (word) => positionMs >= Number(word.start_ms) && positionMs < Number(word.end_ms),
  ) || null;

  const pauseOtherAudio = () => {
    if (sitePlayer?.track && sitePlayer?.stop) sitePlayer.stop();
  };

  const playFrom = (startMs, stopAtMs = null) => {
    const audio = audioRef.current;
    if (!audio || !manifest) return;
    pauseOtherAudio();
    stopAtMsRef.current = stopAtMs;
    audio.currentTime = Math.max(0, startMs) / 1000;
    setPositionMs(Math.max(0, startMs));
    audio.play().catch(() => setError("Press play again to start narration."));
  };

  const toggleChapter = () => {
    const audio = audioRef.current;
    if (!audio || !manifest) return;
    if (!audio.paused) {
      audio.pause();
      return;
    }
    pauseOtherAudio();
    stopAtMsRef.current = null;
    if (audio.ended) audio.currentTime = 0;
    audio.play().catch(() => setError("Press play again to start narration."));
  };

  const onTimeUpdate = () => {
    const audio = audioRef.current;
    if (!audio) return;
    const nextMs = Math.round(audio.currentTime * 1000);
    setPositionMs(nextMs);
    if (stopAtMsRef.current != null && nextMs >= stopAtMsRef.current) {
      audio.pause();
      stopAtMsRef.current = null;
    }
  };

  const timingFor = (verseNumber) => manifest?.verses?.find(
    (item) => Number(item.verse) === Number(verseNumber),
  );

  const renderVerseText = (verse) => {
    const timing = timingFor(verse.verse);
    const isActive = Number(activeVerse?.verse) === Number(verse.verse);
    const word = isActive ? activeWord : null;
    const text = String(verse.text || "");
    if (!word || !Number.isInteger(word.char_start) || !Number.isInteger(word.char_end)) return text;
    return (
      <>
        {text.slice(0, word.char_start)}
        <mark style={{ background: T.gold, color: T.ink, padding: "1px 2px", borderRadius: 2 }}>
          {text.slice(word.char_start, word.char_end)}
        </mark>
        {text.slice(word.char_end)}
      </>
    );
  };

  return (
    <div aria-busy={loading ? "true" : "false"}>
      <audio
        ref={audioRef}
        src={manifest ? bibleChapterPath(bookName, chapter, "chapter.mp3") : undefined}
        preload="metadata"
        onLoadedMetadata={(event) => setDurationMs(Math.round((event.currentTarget.duration || 0) * 1000))}
        onTimeUpdate={onTimeUpdate}
        onPlay={() => setPlaying(true)}
        onPause={() => setPlaying(false)}
        onEnded={() => { setPlaying(false); setPositionMs(0); stopAtMsRef.current = null; }}
      />

      <div style={{ marginBottom: compact ? 18 : 26, padding: compact ? "12px 14px" : "16px 20px", background: T.bgRise, border: `1px solid ${T.gold}44` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <button
            type="button"
            onClick={toggleChapter}
            disabled={loading || !!error}
            aria-label={playing ? `Pause ${bookName} chapter ${chapter}` : `Play ${bookName} chapter ${chapter}`}
            style={{ minWidth: 104, padding: "10px 14px", background: playing ? "transparent" : T.gold, color: playing ? T.gold : T.ink, border: `1px solid ${T.gold}`, cursor: loading || error ? "not-allowed" : "pointer", fontFamily: "'Cinzel', serif", fontSize: 11, textTransform: "uppercase" }}
          >
            {loading ? "Loading…" : playing ? "Pause" : "Play chapter"}
          </button>
          <label style={{ flex: 1, display: "flex", alignItems: "center", gap: 10 }}>
            <span style={{ position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 }}>Chapter position</span>
            <input
              type="range"
              min="0"
              max={Math.max(durationMs, 1)}
              value={Math.min(positionMs, Math.max(durationMs, 1))}
              disabled={!manifest}
              onChange={(event) => {
                const next = Number(event.target.value);
                if (audioRef.current) audioRef.current.currentTime = next / 1000;
                stopAtMsRef.current = null;
                setPositionMs(next);
              }}
              style={{ width: "100%", accentColor: T.gold }}
            />
          </label>
          <span style={{ minWidth: 78, textAlign: "right", color: T.paperDim, fontFamily: "monospace", fontSize: 12, fontVariantNumeric: "tabular-nums" }}>
            {formatBibleAudioTime(positionMs / 1000)} / {formatBibleAudioTime(durationMs / 1000)}
          </span>
        </div>
        {error && <div role="alert" style={{ marginTop: 10, color: "#ffb4a8", fontSize: 13 }}>{error}</div>}
        {!error && <div aria-live="polite" style={{ marginTop: 8, color: T.paperFaint, fontFamily: "'Cormorant Garamond', serif", fontSize: 14, fontStyle: "italic" }}>Play the chapter, or use the play button beside any verse.</div>}
      </div>

      <div style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: compact ? 19 : 20, lineHeight: 1.75, color: T.paper, maxWidth: compact ? undefined : 760 }}>
        {(verses || []).map((verse) => {
          const timing = timingFor(verse.verse);
          const isActive = Number(activeVerse?.verse) === Number(verse.verse);
          return (
            <div key={verse.verse} style={{ display: "grid", gridTemplateColumns: "34px 1fr", gap: 6, margin: "0 0 10px", padding: "4px 8px", borderLeft: `2px solid ${isActive ? T.gold : "transparent"}`, background: isActive ? `${T.gold}12` : "transparent" }}>
              <button
                type="button"
                disabled={!timing}
                onClick={() => playFrom(Number(timing.start_ms) || 0, Number(timing.speech_end_ms || timing.end_ms) || null)}
                aria-label={`Play ${bookName} ${chapter}, verse ${verse.verse}`}
                title={`Play verse ${verse.verse}`}
                style={{ alignSelf: "start", marginTop: 5, padding: "4px 2px", background: "transparent", border: 0, color: isActive ? T.gold : T.goldDeep, cursor: timing ? "pointer" : "default", fontFamily: "'Cinzel', serif", fontSize: 11 }}
              >
                {isActive && playing ? "❚❚" : "▶"} {verse.verse}
              </button>
              <p style={{ margin: 0 }}>{renderVerseText(verse)}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, { BibleNarratedChapter, BIBLE_AUDIO_BASE });
