import { renderHook, act } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import type { ReactNode } from 'react';
import { PlaybackSyncContext } from '../components/PlaybackSyncProvider';
import type { PlaybackSyncApi } from './usePlaybackSync';
import type { PlaybackCommand } from '../services/playbackSync';
import { usePlaybackStore } from '../store/usePlaybackStore';
import { useTransport } from './useTransport';

function wrapperFor(api: Partial<PlaybackSyncApi>) {
  const value = {
    deviceId: 'this-device',
    devices: [],
    isOwner: false,
    hasRemoteOwner: false,
    remotePlaying: false,
    transferTo: async () => undefined,
    sendCommand: async () => undefined,
    ...api,
  } as PlaybackSyncApi;
  return ({ children }: { children: ReactNode }) => (
    <PlaybackSyncContext.Provider value={value}>{children}</PlaybackSyncContext.Provider>
  );
}

describe('useTransport', () => {
  it('plays here when this device holds the audio', () => {
    usePlaybackStore.setState({ isPlaying: false });
    const sendCommand = vi.fn();
    const { result } = renderHook(() => useTransport(), {
      wrapper: wrapperFor({ isOwner: true, hasRemoteOwner: false, sendCommand }),
    });

    act(() => result.current.toggle());

    expect(sendCommand).not.toHaveBeenCalled();
    expect(usePlaybackStore.getState().isPlaying).toBe(true);
  });

  it('forwards the press when another device holds the audio', () => {
    // A watching device keeps its own audio paused, so what the button does
    // follows the remote state and never the local one.
    usePlaybackStore.setState({ isPlaying: false });
    const sendCommand = vi.fn(async (_command: PlaybackCommand) => undefined);
    const { result } = renderHook(() => useTransport(), {
      wrapper: wrapperFor({ hasRemoteOwner: true, remotePlaying: true, sendCommand }),
    });

    expect(result.current.playing).toBe(true);

    act(() => {
      result.current.toggle();
      result.current.next();
      result.current.seek(30);
    });

    expect(sendCommand.mock.calls.map(([command]) => command)).toEqual([
      { type: 'pause' },
      { type: 'next' },
      { type: 'seek', position: 30 },
    ]);
    // The remote device is the one that stops; this one never started.
    expect(usePlaybackStore.getState().isPlaying).toBe(false);
  });

  it('asks a paused remote device to play, whatever this device was doing', () => {
    usePlaybackStore.setState({ isPlaying: true });
    const sendCommand = vi.fn(async (_command: PlaybackCommand) => undefined);
    const { result } = renderHook(() => useTransport(), {
      wrapper: wrapperFor({ hasRemoteOwner: true, remotePlaying: false, sendCommand }),
    });

    act(() => result.current.toggle());

    expect(sendCommand).toHaveBeenCalledWith({ type: 'play' });
  });

  it('plays here when there is no sync session at all', () => {
    usePlaybackStore.setState({ isPlaying: true });
    const { result } = renderHook(() => useTransport());

    act(() => result.current.pause());

    expect(usePlaybackStore.getState().isPlaying).toBe(false);
  });
});
