import { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router';
import { useClient } from 'hooks/useClient';
import { usePanelChannel } from 'hooks/usePanelChannel';
import { Play, Pause, SkipBack, SkipForward, X, Volume2, VolumeX } from 'lucide-react';
import { useSeekBar, SeekBar } from '../apps/FileViewer/renderers/SeekBar';
import { MusicHeart } from '../apps/Music/MusicHeart';
import { MUSIC_ROOT, MUSIC_CWD_CHANNEL, sortTracks, trackHomePath, type AlbumMeta, type NowPlaying } from '../apps/Music/shared';
import { useMusicPlayer, type PlayerTrack } from './useMusicPlayer';

// Mounted once in the persistent DashboardLayout (outside <Routes>), so it owns the single <audio>
// element and the site-wide play dock — playback survives navigation between routes.

const MUSIC_API = '/api/music';
const fmt = (s: number): string =>
  Number.isFinite(s) && s >= 0 ? `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}` : '0:00';

export const MusicPlayerHost = () => {
  const { token, get, put } = useClient();
  const navigate = useNavigate();
  const [, setCwd] = usePanelChannel<string | null>(MUSIC_CWD_CHANNEL, null);
  const { current, index, queue, playing, toggle, next, prev, setPlaying, close, loadQueue } = useMusicPlayer();
  const audioRef = useRef<HTMLAudioElement>(null);
  const [position, setPosition] = useState(0);
  const [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(() => {
    const v = parseFloat(localStorage.getItem('music.volume') ?? '1');
    return Number.isFinite(v) ? v : 1;
  });
  const [muted, setMuted] = useState(false);
  const { barRef, onSeekDown } = useSeekBar(audioRef, duration);

  const trackKey = current ? `${current.albumRel}/${current.file}` : '';

  // Latest position/duration in refs so persist reads fresh values without re-subscribing. seekToRef
  // holds a pending seek (restore); isRestoringRef suppresses persist during the restore load so it
  // doesn't clobber the saved position with 0; restoredRef makes restore run once.
  const positionRef = useRef(0);
  positionRef.current = position;
  const durationRef = useRef(0);
  durationRef.current = duration;
  const restoredRef = useRef(false);
  const isRestoringRef = useRef(false);
  const seekToRef = useRef<number | null>(null);

  const persist = () => {
    if (!current) return;
    put('/music/now-playing', {
      homePath: trackHomePath(current.albumRel, current.file),
      dir: `Music/${current.albumRel}`,
      title: current.title ?? '',
      artist: current.artist ?? '',
      album: current.albumRel.split('/').pop() ?? '',
      durationSec: Math.round(durationRef.current) || 0,
      positionSec: Math.round(positionRef.current) || 0,
    }).catch(() => {});
  };

  // Restore the saved "currently playing" on first load — paused, at its position — so a reload/return
  // lands back on the track. Skipped when a queue already exists (an in-app nav kept player state).
  useEffect(() => {
    if (restoredRef.current) return;
    restoredRef.current = true;
    if (queue.length) return;
    (async () => {
      const snap = await get<NowPlaying | null>('/music/now-playing').catch(() => null);
      if (!snap?.homePath) return;
      const albumRel = (snap.dir || snap.homePath.split('/').slice(0, -1).join('/')).replace(/^Music\//, '');
      const file = snap.homePath.split('/').pop() ?? '';
      const meta = await get<AlbumMeta>(`/music/meta?path=${encodeURIComponent(albumRel)}`).catch(() => null);
      if (!meta) return;
      const q: PlayerTrack[] = sortTracks(meta.tracks).map((t) => ({ albumRel, file: t.file, title: t.title, artist: t.artist }));
      const idx = Math.max(0, q.findIndex((t) => t.file === file));
      isRestoringRef.current = true;
      seekToRef.current = snap.positionSec > 0 ? snap.positionSec : null;
      loadQueue(q, idx);
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Persist on play/pause + track change (not during the restore load).
  useEffect(() => {
    if (isRestoringRef.current) return;
    persist();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [trackKey, playing]);

  // Heartbeat while playing, so the saved position keeps up.
  useEffect(() => {
    if (!playing || !current) return;
    const id = setInterval(persist, 10000);
    return () => clearInterval(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [playing, trackKey]);

  const withToken = (u: string) => (token ? `${u}${u.includes('?') ? '&' : '?'}token=${encodeURIComponent(token)}` : u);
  const streamUrl = (t: PlayerTrack) =>
    withToken(`${MUSIC_API}/stream?path=${encodeURIComponent(`Music/${t.albumRel}/${t.file}`)}`);
  const coverUrl = (rel: string) => withToken(`${MUSIC_API}/cover?path=${encodeURIComponent(rel)}`);

  // Load the current track when it changes.
  useEffect(() => {
    const a = audioRef.current;
    if (!a || !current) return;
    a.src = streamUrl(current);
    setPosition(0);
    if (playing) void a.play().catch(() => setPlaying(false));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [trackKey]);

  // Play/pause without reloading the source.
  useEffect(() => {
    const a = audioRef.current;
    if (!a || !current) return;
    if (playing) void a.play().catch(() => {});
    else a.pause();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [playing]);

  // Apply volume/mute to the element.
  useEffect(() => {
    if (audioRef.current) audioRef.current.volume = muted ? 0 : volume;
  }, [volume, muted, trackKey]);

  const changeVolume = (e: React.ChangeEvent<HTMLInputElement>) => {
    const v = parseFloat(e.target.value);
    setVolume(v);
    setMuted(v === 0);
    localStorage.setItem('music.volume', String(v));
  };

  const subtitle = current?.artist ?? current?.albumRel.split('/').slice(-2, -1)[0] ?? '';
  const pct = duration ? (position / duration) * 100 : 0;

  // Clicking the track info jumps the /music library to the playing album (and navigates there).
  const openCurrentAlbum = () => {
    if (!current) return;
    setCwd(`${MUSIC_ROOT}/${current.albumRel}`);
    navigate('/music');
  };

  return (
    <>
      <audio
        ref={audioRef}
        preload="metadata"
        onTimeUpdate={(e) => setPosition(e.currentTarget.currentTime)}
        onLoadedMetadata={(e) => {
          setDuration(e.currentTarget.duration);
          // Apply a pending restore seek once the track's duration is known, then re-enable persist.
          if (seekToRef.current != null) {
            e.currentTarget.currentTime = seekToRef.current;
            setPosition(seekToRef.current);
            seekToRef.current = null;
          }
          isRestoringRef.current = false;
        }}
        onEnded={() => next()}
      />
      {current && (
        <div className="fixed inset-x-0 bottom-0 z-50 flex items-center gap-3 border-t border-border bg-card/95 px-4 py-2 backdrop-blur">
          {/* cover + info — click to open this album in /music */}
          <button
            type="button"
            onClick={openCurrentAlbum}
            title="Show in library"
            className="flex min-w-0 cursor-pointer items-center gap-3 rounded text-left transition-opacity hover:opacity-80"
          >
            <div className="flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded bg-muted">
              <img
                src={coverUrl(current.albumRel)}
                alt=""
                className="h-full w-full object-cover"
                onError={(e) => {
                  (e.currentTarget as HTMLImageElement).style.visibility = 'hidden';
                }}
              />
            </div>
            <div className="hidden w-44 shrink-0 sm:block">
              <p className="truncate text-sm font-medium text-foreground">{current.title ?? current.file}</p>
              <p className="truncate text-xs text-muted-foreground">{subtitle}</p>
            </div>
          </button>

          {/* transport */}
          <div className="flex shrink-0 items-center gap-1">
            <button
              type="button"
              onClick={prev}
              disabled={index === 0}
              className="cursor-pointer p-1.5 text-muted-foreground hover:text-foreground disabled:cursor-default disabled:opacity-40"
            >
              <SkipBack size={18} />
            </button>
            <button
              type="button"
              onClick={toggle}
              className="flex h-9 w-9 cursor-pointer items-center justify-center rounded-full bg-primary text-primary-foreground hover:opacity-90 active:scale-95"
            >
              {playing ? <Pause size={18} /> : <Play size={18} className="ml-0.5" />}
            </button>
            <button
              type="button"
              onClick={next}
              disabled={index >= queue.length - 1}
              className="cursor-pointer p-1.5 text-muted-foreground hover:text-foreground disabled:cursor-default disabled:opacity-40"
            >
              <SkipForward size={18} />
            </button>
          </div>

          {/* scrubber + times */}
          <span className="hidden w-10 shrink-0 text-right font-mono text-[10px] tabular-nums text-muted-foreground md:block">
            {fmt(position)}
          </span>
          <div className="min-w-0 flex-1">
            <SeekBar
              barRef={barRef}
              onSeekDown={onSeekDown}
              pct={pct}
              trackClass="bg-muted"
              fillClass="bg-primary"
              thumbClass="border-background"
            />
          </div>
          <span className="hidden w-10 shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground md:block">
            {fmt(duration)}
          </span>

          {/* volume */}
          <div className="hidden shrink-0 items-center gap-1.5 md:flex">
            <button type="button" onClick={() => setMuted((m) => !m)} className="cursor-pointer text-muted-foreground hover:text-foreground">
              {muted || volume === 0 ? <VolumeX size={16} /> : <Volume2 size={16} />}
            </button>
            <input
              type="range"
              min={0}
              max={1}
              step={0.01}
              value={muted ? 0 : volume}
              onChange={changeVolume}
              className="h-1 w-16 cursor-pointer accent-primary"
            />
          </div>

          <MusicHeart
            kind="track"
            favKey={trackHomePath(current.albumRel, current.file)}
            size={18}
            className="shrink-0 p-1.5"
          />

          <button type="button" onClick={close} className="shrink-0 cursor-pointer p-1.5 text-muted-foreground hover:text-foreground">
            <X size={16} />
          </button>
        </div>
      )}
    </>
  );
};
