import { useEffect, useState } from 'react';
import type { LyricLine } from './lyrics';
import { activeLineIndex, parseLyrics } from './lyrics';
import { getPlayerTime, subscribePlayerTime } from 'officerdev';

export type UseLyrics = {
  loading: boolean;
  /** null while loading, and when the track has none. */
  lines: LyricLine[] | null;
  synced: boolean;
};

/**
 * Fetch + parse the current track's lyrics. Gated on `enabled` so nothing is requested until the pane
 * is actually open — the dock lives on every screen and most listening happens with it closed.
 *
 * Deliberately NOT gated on an index "has lyrics" flag the way the mobile app does: the web player's
 * queue carries only what it needs to stream, and a 404 for a track without lyrics is cheaper than
 * threading that flag through every producer of a queue.
 *
 * Auth goes in the query string rather than a header, matching how this component already builds its
 * /stream and /cover URLs.
 */
export const useLyrics = (albumRel: string, file: string, enabled: boolean, token: string | null): UseLyrics => {
  const [state, setState] = useState<UseLyrics>({ loading: false, lines: null, synced: false });

  useEffect(() => {
    if (!enabled || !albumRel || !file) {
      setState({ loading: false, lines: null, synced: false });
      return;
    }
    const url =
      `/api/music/lyrics?path=${encodeURIComponent(albumRel)}&file=${encodeURIComponent(file)}` +
      (token ? `&token=${encodeURIComponent(token)}` : '');

    const ctrl = new AbortController();
    setState({ loading: true, lines: null, synced: false });
    fetch(url, { signal: ctrl.signal })
      .then(async (res) => {
        // 404 is the ordinary "this track has no lyrics" answer, not an error worth surfacing.
        if (!res.ok) return setState({ loading: false, lines: null, synced: false });
        const { synced, lines } = parseLyrics(await res.text());
        setState({ loading: false, lines, synced });
      })
      .catch(() => {
        if (!ctrl.signal.aborted) setState({ loading: false, lines: null, synced: false });
      });

    return () => ctrl.abort();
  }, [albumRel, file, enabled, token]);

  return state;
};

/**
 * Index of the line to highlight, driven by the engine's position feed.
 *
 * The feed ticks every animation frame; this re-renders only when the index actually moves — React bails
 * out of an identical setState — so a synced sheet repaints about once a line instead of sixty times a
 * second, even though it lives nowhere near the component that owns the clock.
 */
export const useActiveLyricIndex = (lines: LyricLine[] | null, synced: boolean): number => {
  const [index, setIndex] = useState(-1);

  useEffect(() => {
    if (!synced || !lines) {
      setIndex(-1);
      return;
    }
    setIndex(activeLineIndex(lines, getPlayerTime()));
    return subscribePlayerTime((sec) => {
      const next = activeLineIndex(lines, sec);
      setIndex((prev) => (prev === next ? prev : next));
    });
  }, [lines, synced]);

  return index;
};
