import { create } from 'zustand';

interface TrackPlaybackState {
  isPlaying: boolean;
  currentTime: number;
  startTime: number;
  endTime: number;
  playbackSpeed: number;
  loop: boolean;
  loopStart: number;
  loopEnd: number;

  play: () => void;
  pause: () => void;
  stop: () => void;
  setCurrentTime: (time: number) => void;
  setPlaybackSpeed: (speed: number) => void;
  toggleLoop: () => void;
  setLoopSection: (start: number, end: number) => void;
  setTimeRange: (start: number, end: number) => void;
  syncToRangeStart: () => void;
  reset: () => void;
}

let animationFrameId: number | null = null;
let lastFrameTime: number | null = null;

function clearAnimation(): void {
  if (animationFrameId != null) {
    cancelAnimationFrame(animationFrameId);
    animationFrameId = null;
  }
  lastFrameTime = null;
}

export const useTrackPlaybackStore = create<TrackPlaybackState>()((set, get) => {
  const animate = (): void => {
    const state = get();
    if (!state.isPlaying) return;

    const now = performance.now();
    if (lastFrameTime == null) {
      lastFrameTime = now;
    }

    const delta = now - lastFrameTime;
    lastFrameTime = now;

    const advanceMs = delta * state.playbackSpeed;
    let nextTime = state.currentTime + advanceMs;
    const rangeStart = state.loop ? state.loopStart : state.startTime;
    const rangeEnd = state.loop ? state.loopEnd : state.endTime;

    if (nextTime >= rangeEnd) {
      if (state.loop) {
        nextTime = rangeStart;
      } else {
        nextTime = state.endTime;
        set({ currentTime: nextTime, isPlaying: false });
        clearAnimation();
        return;
      }
    }

    set({ currentTime: nextTime });
    animationFrameId = requestAnimationFrame(animate);
  };

  return {
    isPlaying: false,
    currentTime: 0,
    startTime: 0,
    endTime: 0,
    playbackSpeed: 100,
    loop: false,
    loopStart: 0,
    loopEnd: 0,

    play: () => {
      const state = get();
      if (state.endTime <= state.startTime) return;

      if (state.currentTime < state.startTime || state.currentTime > state.endTime) {
        set({ currentTime: state.startTime });
      }

      set({ isPlaying: true });
      clearAnimation();
      animationFrameId = requestAnimationFrame(animate);
    },

    pause: () => {
      clearAnimation();
      set({ isPlaying: false });
    },

    stop: () => {
      clearAnimation();
      set((state) => ({ isPlaying: false, currentTime: state.startTime }));
    },

    setCurrentTime: (time: number) => {
      const { startTime, endTime } = get();
      const clamped = Math.max(startTime, Math.min(endTime, time));
      set({ currentTime: clamped });
    },

    setPlaybackSpeed: (speed: number) => {
      const normalized = Number.isFinite(speed) && speed > 0 ? speed : 1;
      set({ playbackSpeed: normalized });
    },

    toggleLoop: () => {
      set((state) => ({ loop: !state.loop }));
    },

    setLoopSection: (start: number, end: number) => {
      const state = get();
      const clampedStart = Math.max(state.startTime, Math.min(end, start));
      const clampedEnd = Math.min(state.endTime, Math.max(start, end));
      set({ loopStart: clampedStart, loopEnd: clampedEnd });
    },

    setTimeRange: (start: number, end: number) => {
      const safeStart = Number.isFinite(start) ? start : 0;
      const safeEnd = Number.isFinite(end) ? end : safeStart;
      clearAnimation();
      set({
        isPlaying: false,
        startTime: safeStart,
        endTime: safeEnd,
        currentTime: safeStart,
        loopStart: safeStart,
        loopEnd: safeEnd,
      });
    },

    syncToRangeStart: () => {
      clearAnimation();
      set((state) => ({
        isPlaying: false,
        currentTime: state.startTime,
      }));
    },

    reset: () => {
      clearAnimation();
      set({
        isPlaying: false,
        currentTime: 0,
        startTime: 0,
        endTime: 0,
        playbackSpeed: 100,
        loop: false,
        loopStart: 0,
        loopEnd: 0,
      });
    },
  };
});

export const TRACK_PLAYBACK_SPEED_OPTIONS = [1, 5, 10, 25, 50, 100] as const;
