import { writable } from 'svelte/store';
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';

export type SongPlayerState = {
  currentHash: string | null;
  playing: boolean;
  duration: number;
  currentTime: number;
  volume: number;
};

const DEFAULT_STATE: SongPlayerState = {
  currentHash: null,
  playing: false,
  duration: 0,
  currentTime: 0,
  volume: 0.3
};

let audio: HTMLAudioElement | null = null;
let lastPreferredWasBL = false;

export const currentTimeStore = tweened(0, { duration: 60, easing: cubicOut });
const { subscribe, update, set } = writable<SongPlayerState>({ ...DEFAULT_STATE });

function cleanup(): void {
  if (audio) {
    audio.removeEventListener('timeupdate', handleTimeUpdate as any);
    audio.removeEventListener('loadedmetadata', handleMetadata as any);
    audio.removeEventListener('ended', handleEnded as any);
    audio.removeEventListener('pause', handlePaused as any);
    try {
      audio.pause();
    } catch {}
    try {
      audio.currentTime = 0;
    } catch {}
  }
  audio = null;
}

function handleTimeUpdate(): void {
  if (!audio) return;
  currentTimeStore.set(audio.currentTime);
}

function handleMetadata(): void {
  const a = audio;
  if (!a) return;
  const dur = Number.isFinite(a.duration) ? a.duration : 0;
  update((state) => ({ ...state, duration: dur }));
}

function handleEnded(): void {
  update((state) => ({ ...state, playing: false, currentHash: null }));
}

function handlePaused(): void {
  update((state) => ({ ...state, playing: false }));
}

function buildAudioUrl(hash: string, preferBeatLeader: boolean): string {
  const h = hash?.toLowerCase?.() ?? hash;
  // Prefer BeatSaver CDN; optionally allow BeatLeader CDN
  return preferBeatLeader
    ? `https://cdn.songs.beatleader.com/${h}.mp3`
    : `https://eu.cdn.beatsaver.com/${h}.mp3`;
}

export function togglePlay(hash: string, preferBeatLeader = false): void {
  if (!hash) return;
  const url = buildAudioUrl(hash, preferBeatLeader);
  const altUrl = buildAudioUrl(hash, !preferBeatLeader);
  lastPreferredWasBL = preferBeatLeader;

  update((state) => {
    const shouldPlay = hash === state.currentHash ? !state.playing : true;

    const initWithUrl = (initialUrl: string) => {
      cleanup();
      currentTimeStore.set(0);
      audio = new Audio(initialUrl);
      audio.volume = state.volume;
      audio.addEventListener('timeupdate', handleTimeUpdate as any);
      audio.addEventListener('loadedmetadata', handleMetadata as any);
      audio.addEventListener('ended', handleEnded as any);
      audio.addEventListener('pause', handlePaused as any);
      // Fallback to alternate CDN once on error
      let triedFallback = false;
      audio.addEventListener('error', () => {
        if (!audio || triedFallback) return;
        triedFallback = true;
        try {
          const wasPlaying = shouldPlay;
          audio.src = altUrl;
          audio.load();
          if (wasPlaying) audio.play?.();
        } catch {}
      }, { once: false } as any);
    };

    if (hash !== state.currentHash) {
      initWithUrl(url);
    } else if (!audio) {
      initWithUrl(url);
    }

    if (shouldPlay) {
      audio?.play?.();
    } else {
      audio?.pause?.();
    }

    return { ...state, currentHash: hash, playing: shouldPlay };
  });
}

export function setVolume(volume: number): void {
  const clamped = Math.max(0, Math.min(1, Number.isFinite(volume) ? volume : 0.3));
  update((state) => {
    if (audio) audio.volume = clamped;
    return { ...state, volume: clamped };
  });
}

export function reset(): void {
  cleanup();
  set({ ...DEFAULT_STATE });
}

export const songPlayerStore = { subscribe };


