import type { TrackRef, RadioResponse } from '$lib/api/types';
import { api } from '$lib/api/client';

export type PlayerState = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
export type RepeatMode = 'off' | 'all' | 'one';

const VOLUME_KEY = 'minstrel.volume';

export function readStoredVolume(): number {
  try {
    const raw = localStorage.getItem(VOLUME_KEY);
    if (raw == null) return 1;
    const n = Number(raw);
    if (Number.isFinite(n) && n >= 0 && n <= 1) return n;
    return 1;
  } catch {
    return 1;
  }
}

let _queue    = $state<TrackRef[]>([]);
let _index    = $state(0);
let _state    = $state<PlayerState>('idle');
let _position = $state(0);
let _duration = $state(0);
let _volume   = $state(readStoredVolume());
let _shuffle  = $state(false);
let _repeat   = $state<RepeatMode>('off');
let _error    = $state<string | null>(null);

let _audioEl: HTMLAudioElement | null = null;

export const player = {
  get queue()     { return _queue; },
  get index()     { return _index; },
  get current()   { return _queue[_index] as TrackRef | undefined; },
  get state()     { return _state; },
  get isPlaying() { return _state === 'playing'; },
  get position()  { return _position; },
  get duration()  { return _duration; },
  get volume()    { return _volume; },
  get shuffle()   { return _shuffle; },
  get repeat()    { return _repeat; },
  get error()     { return _error; }
};

export function registerAudioEl(el: HTMLAudioElement | null): void {
  _audioEl = el;
}

export function playQueue(tracks: TrackRef[], startIndex = 0): void {
  _queue = tracks;
  if (tracks.length === 0) {
    _index = 0;
    _state = 'idle';
  } else {
    _index = Math.max(0, Math.min(startIndex, tracks.length - 1));
    _state = 'loading';
  }
  _position = 0;
  _duration = 0;
  _error = null;
}

export function togglePlay(): void {
  if (_queue.length === 0) return;
  // "Intent to play" (loading + playing) → paused. Anything else → loading (user
  // wants to play; audio will catch up).
  if (_state === 'playing' || _state === 'loading') {
    _state = 'paused';
  } else {
    _state = 'loading';
  }
}

export function skipNext(): void {
  if (_queue.length === 0) return;
  if (_index + 1 < _queue.length) {
    _index++;
    _position = 0;
    _duration = 0;
    _state = 'loading';
    return;
  }
  // At end of queue.
  if (_repeat === 'all') {
    _index = 0;
    _position = 0;
    _duration = 0;
    _state = 'loading';
  } else {
    // 'off' or 'one' both pause at end on EXPLICIT skip.
    _state = 'paused';
    _position = _duration;
  }
}

export function skipPrev(): void {
  if (_queue.length === 0) return;
  if (_position < 3 && _index > 0) {
    _index--;
    _position = 0;
    _duration = 0;
    _state = 'loading';
  } else {
    // Seek to 0 of the current track.
    _position = 0;
    if (_audioEl) _audioEl.currentTime = 0;
  }
}

export function seekTo(sec: number): void {
  _position = sec;
  if (_audioEl) _audioEl.currentTime = sec;
}

export function setVolume(v: number): void {
  const clamped = Math.max(0, Math.min(1, v));
  _volume = clamped;
  try { localStorage.setItem(VOLUME_KEY, String(clamped)); } catch { /* ignore */ }
}

export function reportTimeUpdate(sec: number): void {
  _position = sec;
}

export function reportDuration(sec: number): void {
  _duration = Number.isFinite(sec) ? sec : 0;
}

// --- Shuffle (with injectable RNG for tests) ---

let _rng: () => number = Math.random;
export function __setShuffleRng(fn: () => number): void { _rng = fn; }

export function toggleShuffle(): void {
  _shuffle = !_shuffle;
  if (_shuffle && _index + 1 < _queue.length) {
    // Fisher-Yates shuffle over _queue[_index+1 .. end].
    const arr = _queue.slice();
    for (let j = arr.length - 1; j > _index; j--) {
      const r = Math.floor(_rng() * (j - _index)) + _index + 1;
      [arr[j], arr[r]] = [arr[r], arr[j]];
    }
    _queue = arr;
  }
}

// --- Repeat ---

export function cycleRepeat(): void {
  _repeat = _repeat === 'off' ? 'all' : _repeat === 'all' ? 'one' : 'off';
}

export function reportStateFromAudio(
  event: 'playing' | 'paused' | 'waiting' | 'ended' | 'error',
  detail?: string
): void {
  switch (event) {
    case 'playing':
      _state = 'playing';
      _error = null;
      return;
    case 'paused':
      _state = 'paused';
      return;
    case 'waiting':
      _state = 'loading';
      return;
    case 'error':
      _state = 'error';
      _error = detail ?? 'Playback failed.';
      return;
    case 'ended':
      if (_repeat === 'one') {
        _position = 0;
        _state = 'loading';
        if (_audioEl) _audioEl.currentTime = 0;
        return;
      }
      if (_index + 1 < _queue.length) {
        _index++;
        _position = 0;
        _duration = 0;
        _state = 'loading';
        return;
      }
      if (_repeat === 'all') {
        _index = 0;
        _position = 0;
        _duration = 0;
        _state = 'loading';
      } else {
        _state = 'paused';
        _position = _duration;
      }
      return;
  }
}

export function enqueueTrack(t: TrackRef): void {
  _queue = [..._queue, t];
  if (_state === 'idle') _index = 0;
}

export function enqueueTracks(ts: TrackRef[]): void {
  if (ts.length === 0) return;
  _queue = [..._queue, ...ts];
  if (_state === 'idle') _index = 0;
}

export async function playRadio(seedTrackId: string): Promise<void> {
  const resp = await api.get<RadioResponse>(
    `/api/radio?seed_track=${encodeURIComponent(seedTrackId)}`
  );
  if (resp.tracks.length > 0) playQueue(resp.tracks, 0);
}
