import { createSelector } from 'reselect';
import get from 'lodash/get';
import createReducer from 'utils/createReducer';
import { getSelectedTab, updateSelection } from './tab';
import * as actions from './actions';

export const getNetworks = state => state.networks;

export const getCurrentNick = createSelector(
  getNetworks,
  getSelectedTab,
  (networks, tab) => {
    if (!networks[tab.network]) {
      return;
    }
    const { editedNick } = networks[tab.network];
    if (editedNick === null) {
      return networks[tab.network].nick;
    }
    return editedNick;
  }
);

export const getCurrentNetworkName = createSelector(
  getNetworks,
  getSelectedTab,
  (networks, tab) => get(networks, [tab.network, 'name'])
);

export const getCurrentNetworkError = createSelector(
  getNetworks,
  getSelectedTab,
  (networks, tab) => get(networks, [tab.network, 'error'], null)
);

export default createReducer(
  {},
  {
    [actions.CONNECT](state, { host, nick, name }) {
      if (!state[host]) {
        state[host] = {
          nick,
          editedNick: null,
          name: name || host,
          connected: false,
          error: null,
          features: {}
        };
      }
    },

    [actions.DISCONNECT](state, { network }) {
      delete state[network];
    },

    [actions.SET_NETWORK_NAME](state, { network, name }) {
      state[network].name = name;
    },

    [actions.SET_NICK](state, { network, nick, editing }) {
      if (editing) {
        state[network].editedNick = nick;
      } else if (nick === '') {
        state[network].editedNick = null;
      }
    },

    [actions.socket.NICK](state, { network, oldNick, newNick }) {
      if (!oldNick || oldNick === state[network].nick) {
        state[network].nick = newNick;
        state[network].editedNick = null;
      }
    },

    [actions.socket.NICK_FAIL](state, { network }) {
      state[network].editedNick = null;
    },

    [actions.INIT](state, { networks }) {
      if (networks) {
        networks.forEach(
          ({ host, name = host, nick, connected, error, features = {} }) => {
            state[host] = {
              name,
              nick,
              connected,
              error,
              features,
              editedNick: null
            };
          }
        );
      }
    },

    [actions.socket.CONNECTION_UPDATE](state, { network, connected, error }) {
      if (state[network]) {
        state[network].connected = connected;
        state[network].error = error;
      }
    },

    [actions.socket.FEATURES](state, { network, features }) {
      const srv = state[network];
      if (srv) {
        srv.features = features;

        if (features.NETWORK && srv.name === network) {
          srv.name = features.NETWORK;
        }
      }
    }
  }
);

export function connect(config) {
  return {
    type: actions.CONNECT,
    ...config,
    socket: {
      type: 'connect',
      data: config
    }
  };
}

export function disconnect(network) {
  return dispatch => {
    dispatch({
      type: actions.DISCONNECT,
      network,
      socket: {
        type: 'quit',
        data: { network }
      }
    });
    dispatch(updateSelection());
  };
}

export function reconnect(network, settings) {
  return {
    type: actions.RECONNECT,
    network,
    settings,
    socket: {
      type: 'reconnect',
      data: {
        ...settings,
        network
      }
    }
  };
}

export function whois(user, network) {
  return {
    type: actions.WHOIS,
    user,
    network,
    socket: {
      type: 'whois',
      data: { user, network }
    }
  };
}

export function away(message, network) {
  return {
    type: actions.AWAY,
    message,
    network,
    socket: {
      type: 'away',
      data: { message, network }
    }
  };
}

export function setNick(nick, network, editing) {
  nick = nick.trim().replace(' ', '');

  const action = {
    type: actions.SET_NICK,
    nick,
    network,
    editing
  };

  if (!editing && nick !== '') {
    action.socket = {
      type: 'nick',
      data: {
        newNick: nick,
        network
      }
    };
  }

  return action;
}

export function isValidNetworkName(name) {
  return name.trim() !== '';
}

export function setNetworkName(name, network) {
  const action = {
    type: actions.SET_NETWORK_NAME,
    name,
    network
  };

  if (isValidNetworkName(name)) {
    action.socket = {
      type: 'set_network_name',
      data: {
        name,
        network
      },
      debounce: {
        delay: 500,
        key: `network_name:${network}`
      }
    };
  }

  return action;
}