import { useMemo } from 'react';
import { usePlaybackStore } from '../store/usePlaybackStore';
import { useOptionalPlaybackSync } from '../components/PlaybackSyncProvider';

/**
 * The transport every control set should call.
 *
 * While another device holds the audio, a press has to travel to that device
 * instead of starting a second stream here. Controls that reach into the
 * playback store directly work on the desktop and silently do nothing useful
 * from a phone, so there is one place that makes the choice.
 */
export interface Transport {
  play: () => void;
  pause: () => void;
  toggle: () => void;
  next: () => void;
  prev: () => void;
  seek: (seconds: number) => void;
  /** True when the presses are being forwarded rather than played here. */
  remote: boolean;
  /**
   * Whether the audio is playing, wherever it is. A watching device holds its
   * own store paused, so a control that reads `isPlaying` from the store draws a
   * Play button while the desktop plays — and then sends `play` when the
   * listener presses it, which is why pausing from a phone never worked. Every
   * control reads this instead.
   */
  playing: boolean;
}

export function useTransport(): Transport {
  const sync = useOptionalPlaybackSync();
  const hasRemoteOwner = sync?.hasRemoteOwner ?? false;
  const remotePlaying = sync?.remotePlaying ?? false;
  const sendCommand = sync?.sendCommand;
  const localPlaying = usePlaybackStore((state) => state.isPlaying);
  const playing = hasRemoteOwner ? remotePlaying : localPlaying;

  return useMemo(() => {
    const local = () => usePlaybackStore.getState();
    const away = hasRemoteOwner && sendCommand !== undefined;
    // The owning device may hold the session without listening on it — a killed
    // tab keeps it until the sweep. The backend answers that with a 409, and a
    // rejected press is not worth an unhandled rejection.
    const send = (command: Parameters<NonNullable<typeof sendCommand>>[0]) =>
      void Promise.resolve(sendCommand!(command)).catch(() => undefined);
    return {
      play: () => (away ? send({ type: 'play' }) : local().play()),
      pause: () => (away ? send({ type: 'pause' }) : local().pause()),
      toggle: () => {
        if (away) send({ type: playing ? 'pause' : 'play' });
        else if (playing) local().pause();
        else local().play();
      },
      next: () => (away ? send({ type: 'next' }) : local().next()),
      prev: () => (away ? send({ type: 'prev' }) : local().prev()),
      seek: (seconds: number) =>
        away ? send({ type: 'seek', position: seconds }) : local().setPosition(seconds),
      remote: away,
      playing,
    };
  }, [hasRemoteOwner, playing, sendCommand]);
}
