import {
  ActionSongInfoFetched,
  ActionStateSetLocal,
  ActionStateSetRemote,
  ActionTypeLocal,
  ActionTypeRemote,
  AnyAction,
} from '../actions';
import { isFromOurselves, isMaster, willBeMaster } from '../selectors';
import { MusicPlayer } from '../types/state';
import { GlobalState } from './types';

export const nullPlayer: MusicPlayer = {
  songId: null,
  playing: false,
  currentTime: 0,
  seekTime: -1,
  master: '',
  queue: [],
};

export const initialState: GlobalState = {
  songInfo: null,
  player: nullPlayer,
  clientList: [],
  myClientName: '',
};

function shouldSetSeekTime(state: GlobalState, action: ActionStateSetRemote): boolean {
  return willBeMaster(state, action) && !(isMaster(state) && isFromOurselves(state, action));
}

function onRemoteStateSet(state: GlobalState, action: ActionStateSetRemote): GlobalState {
  const nextPlayer = action.payload ?? nullPlayer;
  const seekTime = shouldSetSeekTime(state, action) ? nextPlayer.seekTime : -1;

  const nextPlayerWithSeekTime: MusicPlayer = { ...nextPlayer, seekTime };

  return { ...state, player: nextPlayerWithSeekTime };
}

function onLocalStateSet(state: GlobalState, action: ActionStateSetLocal): GlobalState {
  const nextPlayer: MusicPlayer = { ...state.player, ...action.payload };

  if (isMaster(state)) {
    return { ...state, player: nextPlayer };
  }

  if (willBeMaster(state, action)) {
    return {
      ...state,
      player: {
        ...nextPlayer,
        seekTime: nextPlayer.currentTime,
      },
    };
  }

  return state;
}

function onSongFetched(state: GlobalState, action: ActionSongInfoFetched): GlobalState {
  const nextState: GlobalState = { ...state, songInfo: action.payload.song };
  if (!action.payload.replace) {
    return nextState;
  }
  return {
    ...nextState,
    player: {
      ...state.player,
      playing: !!action.payload.song,
      songId: action.payload.song?.id ?? null,
      seekTime: 0,
    },
  };
}

export function globalReducer(state: GlobalState, action: AnyAction): GlobalState {
  switch (action.type) {
    case ActionTypeRemote.StateSet:
      return onRemoteStateSet(state, action);

    case ActionTypeRemote.ClientListUpdated:
      return { ...state, clientList: action.payload };

    case ActionTypeLocal.NameSet:
      return { ...state, myClientName: action.payload };

    case ActionTypeLocal.StateSet:
      return onLocalStateSet(state, action);

    case ActionTypeLocal.Seeked:
      return {
        ...state,
        player: {
          ...state.player,
          currentTime: action.payload,
          seekTime: isMaster(state) ? action.payload : state.player.seekTime,
        },
      };

    case ActionTypeLocal.MasterSet:
      if (action.payload) {
        return {
          ...state,
          player: {
            ...state.player,
            master: action.payload,
            seekTime: state.player.currentTime,
          },
        };
      }

      return {
        ...state,
        player: {
          ...state.player,
          master: state.myClientName,
          seekTime: state.player.currentTime,
          playing: false,
        },
      };

    case ActionTypeLocal.PlayPaused:
      if (!isMaster(state)) {
        return state;
      }
      return { ...state, player: { ...state.player, playing: !state.player.playing } };

    case ActionTypeLocal.SongInfoFetched:
      return onSongFetched(state, action);

    default:
      return state;
  }
}
