import type { VideoPlaybackEvent, VideoPlaybackState } from '../../shared/types';

function nowMs(): number {
  return Date.now();
}

function clamp(v: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, v));
}

export class VideoPlaybackStore {
  private state: VideoPlaybackState = {
    revision: 1,
    serverNowMs: nowMs(),
    targetAssetId: null,
    playing: false,
    playbackRate: 1,
    anchorServerMs: nowMs(),
    anchorVideoTimeSec: 0,
  };

  getState(): VideoPlaybackState {
    return { ...this.state, serverNowMs: nowMs() };
  }

  dispatch(event: VideoPlaybackEvent): VideoPlaybackState {
    const s = this.getState();
    const curTime = computeTimeSec(s, s.serverNowMs);
    const bump = (patch: Omit<VideoPlaybackState, 'revision' | 'serverNowMs'>): VideoPlaybackState => ({
      ...patch,
      revision: s.revision + 1,
      serverNowMs: nowMs(),
    });

    switch (event.kind) {
      case 'target.set': {
        const nextTarget = event.assetId ?? null;
        const next: Omit<VideoPlaybackState, 'revision' | 'serverNowMs'> = {
          ...s,
          targetAssetId: nextTarget,
          playing: Boolean(event.autostart) && nextTarget !== null,
          playbackRate: s.playbackRate,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: 0,
        };
        this.state = bump(next);
        return this.state;
      }
      case 'play': {
        this.state = bump({
          ...s,
          playing: true,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: curTime,
        });
        return this.state;
      }
      case 'pause': {
        this.state = bump({
          ...s,
          playing: false,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: curTime,
        });
        return this.state;
      }
      case 'stop': {
        this.state = bump({
          ...s,
          playing: false,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: 0,
        });
        return this.state;
      }
      case 'seek': {
        const t = clamp(event.timeSec, 0, 1_000_000);
        this.state = bump({
          ...s,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: t,
        });
        return this.state;
      }
      case 'rate.set': {
        const rate = clamp(event.rate, 0.25, 3);
        this.state = bump({
          ...s,
          playbackRate: rate,
          anchorServerMs: s.serverNowMs,
          anchorVideoTimeSec: curTime,
        });
        return this.state;
      }
      default: {
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const _x: never = event;
        return this.state;
      }
    }
  }
}

export function computeTimeSec(state: VideoPlaybackState, atServerNowMs: number): number {
  if (!state.playing) return state.anchorVideoTimeSec;
  const dt = Math.max(0, atServerNowMs - state.anchorServerMs);
  return state.anchorVideoTimeSec + (dt / 1000) * state.playbackRate;
}
