import React, { useCallback, useEffect, useState, useRef, memo, useMemo } from 'react';
import {
  Play, Pause, SkipBack, SkipForward,
  ChevronDown, Repeat, Repeat1, Square, Music, MicVocal
} from 'lucide-react';
import { usePlayerStore } from '../store/playerStore';
import { useLyricsStore } from '../store/lyricsStore';
import { buildCoverArtUrl, coverArtCacheKey, getArtistInfo } from '../api/subsonic';
import CachedImage, { useCachedUrl } from './CachedImage';
import { useTranslation } from 'react-i18next';

function formatTime(seconds: number): string {
  if (!seconds || isNaN(seconds)) return '0:00';
  const m = Math.floor(seconds / 60);
  const s = Math.floor(seconds % 60);
  return `${m}:${s.toString().padStart(2, '0')}`;
}

function MarqueeTitle({ title }: { title: string }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const textRef = useRef<HTMLSpanElement>(null);
  const [scrollAmount, setScrollAmount] = useState(0);

  const measure = useCallback(() => {
    const container = containerRef.current;
    const text = textRef.current;
    if (!container || !text) return;
    // Temporarily make span inline-block to get its natural width
    text.style.display = 'inline-block';
    const textWidth = text.getBoundingClientRect().width;
    text.style.display = '';
    const overflow = textWidth - container.clientWidth;
    setScrollAmount(overflow > 4 ? Math.ceil(overflow) : 0);
  }, []);

  useEffect(() => {
    measure();
    const ro = new ResizeObserver(measure);
    if (containerRef.current) ro.observe(containerRef.current);
    return () => ro.disconnect();
  }, [title, measure]);

  return (
    <div ref={containerRef} className="fs-title-wrap">
      <span
        ref={textRef}
        className={scrollAmount > 0 ? 'fs-title-marquee' : ''}
        style={scrollAmount > 0 ? { '--scroll-amount': `-${scrollAmount}px` } as React.CSSProperties : {}}
      >
        {title}
      </span>
    </div>
  );
}

// ─── Crossfading blurred background ───────────────────────────────────────────
const FsBg = memo(function FsBg({ url }: { url: string }) {
  const [layers, setLayers] = useState<Array<{ url: string; id: number; visible: boolean }>>(() =>
    url ? [{ url, id: 0, visible: true }] : []
  );
  const counterRef = useRef(1);

  useEffect(() => {
    if (!url) return;
    let cancelled = false;
    const id = counterRef.current++;
    // Preload the image before starting the crossfade — prevents a blank flash
    // between the old and new layer while the browser decodes the image.
    const img = new Image();
    img.onload = img.onerror = () => {
      if (cancelled) return;
      setLayers(prev => [...prev, { url, id, visible: false }]);
      requestAnimationFrame(() => {
        if (cancelled) return;
        setLayers(prev => prev.map(l => ({ ...l, visible: l.id === id })));
        setTimeout(() => {
          if (!cancelled) setLayers(prev => prev.filter(l => l.id === id));
        }, 800);
      });
    };
    img.src = url;
    return () => { cancelled = true; };
  }, [url]);

  return (
    <>
      {layers.map(layer => (
        <div
          key={layer.id}
          className="fs-bg"
          style={{ backgroundImage: `url(${layer.url})`, opacity: layer.visible ? 1 : 0 }}
          aria-hidden="true"
        />
      ))}
    </>
  );
});

// ─── Progress bar (isolated — re-renders every tick) ──────────────────────────
const FsProgress = memo(function FsProgress({ duration }: { duration: number }) {
  const progress    = usePlayerStore(s => s.progress);
  const buffered    = usePlayerStore(s => s.buffered);
  const currentTime = usePlayerStore(s => s.currentTime);
  const seek        = usePlayerStore(s => s.seek);

  const handleSeek = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => seek(parseFloat(e.target.value)),
    [seek]
  );

  const pct = progress * 100;
  const buf = Math.max(pct, buffered * 100);

  return (
    <div className="fs-progress-wrap">
      <span className="fs-time">{formatTime(currentTime)}</span>
      <div className="fs-progress-bar">
        <input
          type="range" min={0} max={1} step={0.001}
          value={progress}
          onChange={handleSeek}
          style={{
            '--pct': `${pct}%`,
            '--buf': `${buf}%`,
          } as React.CSSProperties}
          aria-label="progress"
        />
      </div>
      <span className="fs-time">{formatTime(duration)}</span>
    </div>
  );
});

// ─── Play/Pause button (isolated — subscribes to isPlaying only) ──────────────
const FsPlayBtn = memo(function FsPlayBtn() {
  const { t } = useTranslation();
  const isPlaying  = usePlayerStore(s => s.isPlaying);
  const togglePlay = usePlayerStore(s => s.togglePlay);
  return (
    <button className="fs-btn fs-btn-play" onClick={togglePlay} aria-label={isPlaying ? t('player.pause') : t('player.play')}>
      {isPlaying ? <Pause size={25} /> : <Play size={25} fill="currentColor" />}
    </button>
  );
});

// ─── Main component ────────────────────────────────────────────────────────────
interface FullscreenPlayerProps {
  onClose: () => void;
}

export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) {
  const { t } = useTranslation();
  const currentTrack = usePlayerStore(s => s.currentTrack);
  const repeatMode   = usePlayerStore(s => s.repeatMode);
  const next         = usePlayerStore(s => s.next);
  const previous     = usePlayerStore(s => s.previous);
  const stop         = usePlayerStore(s => s.stop);
  const toggleRepeat = usePlayerStore(s => s.toggleRepeat);

  const showLyrics      = useLyricsStore(s => s.showLyrics);
  const activeTab       = useLyricsStore(s => s.activeTab);
  const isQueueVisible  = usePlayerStore(s => s.isQueueVisible);
  const toggleQueue     = usePlayerStore(s => s.toggleQueue);

  const duration = currentTrack?.duration ?? 0;
  // buildCoverArtUrl generates a new salt on every call — must be memoized
  // to prevent useCachedUrl from re-fetching on every progress re-render (100 ms).
  const coverUrl = useMemo(() => currentTrack?.coverArt ? buildCoverArtUrl(currentTrack.coverArt, 800) : '', [currentTrack?.coverArt]);
  const coverKey = useMemo(() => currentTrack?.coverArt ? coverArtCacheKey(currentTrack.coverArt, 800) : '', [currentTrack?.coverArt]);
  // No fetchUrl fallback for the background — we only want stable blob URLs
  // to avoid a double crossfade (fetchUrl → blobUrl for the same image).
  const resolvedCoverUrl = useCachedUrl(coverUrl, coverKey, false);

  // Fetch artist image for background — fall back to cover art if unavailable
  const [artistBgUrl, setArtistBgUrl] = useState<string>('');
  useEffect(() => {
    setArtistBgUrl('');
    const artistId = currentTrack?.artistId;
    if (!artistId) return;
    let cancelled = false;
    getArtistInfo(artistId).then(info => {
      if (!cancelled && info.largeImageUrl) setArtistBgUrl(info.largeImageUrl);
    }).catch(() => {});
    return () => { cancelled = true; };
  }, [currentTrack?.artistId]);

  const bgUrl = artistBgUrl || resolvedCoverUrl;

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  return (
    <div className="fs-player" role="dialog" aria-modal="true" aria-label={t('player.fullscreen')}>

      {/* Layer 1 — blurred artist image */}
      <FsBg url={bgUrl} />
      <div className="fs-bg-overlay" aria-hidden="true" />

      {/* Close */}
      <button className="fs-close" onClick={onClose} aria-label={t('player.closeFullscreen')}>
        <ChevronDown size={28} />
      </button>


      {/* Center stage — everything vertically + horizontally centered */}
      <div className="fs-stage">

        <p className="fs-artist">{currentTrack?.artist ?? '—'}</p>

        <div className="fs-cover-wrap">
          {coverUrl ? (
            <CachedImage
              src={coverUrl}
              cacheKey={coverKey}
              alt={`${currentTrack?.album} Cover`}
              className="fs-cover"
            />
          ) : (
            <div className="fs-cover fs-cover-placeholder"><Music size={72} /></div>
          )}
        </div>

        <div className="fs-track-info">
          <MarqueeTitle title={currentTrack?.title ?? '—'} />
          <p className="fs-album">
            {currentTrack?.album ?? ''}
            {currentTrack?.year ? ` · ${currentTrack.year}` : ''}
          </p>
          {(currentTrack?.bitRate || currentTrack?.suffix) && (
            <span className="fs-codec">
              {[
                currentTrack.suffix?.toUpperCase(),
                currentTrack.bitRate ? `${currentTrack.bitRate} kbps` : ''
              ].filter(Boolean).join(' · ')}
            </span>
          )}
        </div>

        <FsProgress duration={duration} />

        <div className="fs-controls">
          <button className="fs-btn fs-btn-sm" onClick={stop} aria-label="Stop">
            <Square size={14} fill="currentColor" />
          </button>
          <button className="fs-btn" onClick={() => previous()} aria-label={t('player.prev')}>
            <SkipBack size={20} />
          </button>
          <FsPlayBtn />
          <button className="fs-btn" onClick={() => next()} aria-label={t('player.next')}>
            <SkipForward size={20} />
          </button>
          <button
            className={`fs-btn fs-btn-sm ${repeatMode !== 'off' ? 'active' : ''}`}
            onClick={toggleRepeat}
            aria-label={t('player.repeat')}
          >
            {repeatMode === 'one' ? <Repeat1 size={14} /> : <Repeat size={14} />}
          </button>
          <button
            className={`fs-btn fs-btn-sm ${activeTab === 'lyrics' && isQueueVisible ? 'active' : ''}`}
            onClick={() => { if (!isQueueVisible) toggleQueue(); showLyrics(); }}
            aria-label={t('player.lyrics')}
            data-tooltip={t('player.lyrics')}
          >
            <MicVocal size={14} />
          </button>
        </div>

      </div>
    </div>
  );
}
