/** @module swisstennis/state */
import { call, put, takeEvery, select } from 'redux-saga/effects'
import { SZTM_API } from '../local-constants'
import api from '../api'
import moment from 'moment'

/**
 * state.js
 *
 * Collection of everything which has to do with state changes.
 **/

/** actionTypes define what actions are handeled by the reducer. */
export const actions = {
  matchesSetFilter: (data) => {
    return {
      type: 'SWISSTENNIS/SET_FILTER',
      data,
    }
  },
  filterMatches: (data) => {
    return {
      type: 'SWISSTENNIS/FILTER_MATCHES',
      data,
    }
  },
  filterMatchesDone: (data) => {
    return {
      type: 'SWISSTENNIS/FILTER_MATCHES_DONE',
      data,
    }
  },
  filterPlayers: (data) => {
    return {
      type: 'SWISSTENNIS/FILTER_PLAYERS',
      data,
    }
  },
  filterPlayersDone: (data) => {
    return {
      type: 'SWISSTENNIS/FILTER_PLAYERS_DONE',
      data,
    }
  },
  fileListRequest: () => {
    return {
      type: 'SWISSTENNIS/FILE_LIST_REQUEST',
    }
  },
  fileListSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/FILE_LIST_SUCCESS',
      data,
    }
  },
  fileListFailure: (error) => {
    return {
      type: 'SWISSTENNIS/FILE_LIST_FAILURE',
      error,
    }
  },
  loginRequest: (data) => {
    return {
      type: 'SWISSTENNIS/LOGIN_REQUEST',
      data,
    }
  },
  loginSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/LOGIN_SUCCESS',
      data,
    }
  },
  loginFailure: (error) => {
    return {
      type: 'SWISSTENNIS/LOGIN_FAILURE',
      error,
    }
  },
  downloadPlayerListRequest: (data) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST',
      data,
    }
  },
  downloadPlayerListSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_SUCCESS',
      data,
    }
  },
  downloadPlayerListFailure: (error) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_FAILURE',
      error,
    }
  },
  parsePlayerListRequest: (data) => {
    return {
      type: 'SWISSTENNIS/PARSE_PLAYERLIST_REQUEST',
      data,
    }
  },
  parsePlayerListSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/PARSE_PLAYERLIST_SUCCESS',
      data,
    }
  },
  parsePlayerListFailure: (error) => {
    return {
      type: 'SWISSTENNIS/PARSE_PLAYERLIST_FAILURE',
      error,
    }
  },
  deletePlayerListRequest: (data) => {
    return {
      type: 'SWISSTENNIS/DELETE_PLAYERLIST_REQUEST',
      data,
    }
  },
  deletePlayerListSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/DELETE_PLAYERLIST_SUCCESS',
      data,
    }
  },
  deletePlayerListFailure: (error) => {
    return {
      type: 'SWISSTENNIS/DELETE_PLAYERLIST_FAILURE',
      error,
    }
  },
  downloadCalendarRequest: (data) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_REQUEST',
      data,
    }
  },
  downloadCalendarSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_SUCCESS',
      data,
    }
  },
  downloadCalendarFailure: (error) => {
    return {
      type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_FAILURE',
      error,
    }
  },
  parseCalendarRequest: (data) => {
    return {
      type: 'SWISSTENNIS/PARSE_CALENDAR_REQUEST',
      data,
    }
  },
  parseCalendarSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/PARSE_CALENDAR_SUCCESS',
      data,
    }
  },
  parseCalendarFailure: (error) => {
    return {
      type: 'SWISSTENNIS/PARSE_CALENDAR_FAILURE',
      error,
    }
  },
  deleteCalendarRequest: (data) => {
    return {
      type: 'SWISSTENNIS/DELETE_CALENDAR_REQUEST',
      data,
    }
  },
  deleteCalendarSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/DELETE_CALENDAR_SUCCESS',
      data,
    }
  },
  deleteCalendarFailure: (error) => {
    return {
      type: 'SWISSTENNIS/DELETE_CALENDAR_FAILURE',
      error,
    }
  },
  getPlayerListRequest: () => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLIST_REQUEST',
    }
  },
  getPlayerListSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLIST_SUCCESS',
      data,
    }
  },
  getPlayerListFailure: (error) => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLIST_FAILURE',
      error,
    }
  },
  getPlayerListsRequest: () => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLISTS_REQUEST',
    }
  },
  getPlayerListsSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLISTS_SUCCESS',
      data,
    }
  },
  getPlayerListsFailure: (error) => {
    return {
      type: 'SWISSTENNIS/GET_PLAYERLISTS_FAILURE',
      error,
    }
  },
  getCalendarRequest: (data) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDAR_REQUEST',
      data,
    }
  },
  getCalendarSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDAR_SUCCESS',
      data,
    }
  },
  getCalendarFailure: (error) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDAR_FAILURE',
      error,
    }
  },
  getCalendarsRequest: (data) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDARS_REQUEST',
      data,
    }
  },
  getCalendarsSuccess: (data) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDARS_SUCCESS',
      data,
    }
  },
  getCalendarsFailure: (error) => {
    return {
      type: 'SWISSTENNIS/GET_CALENDARS_FAILURE',
      error,
    }
  },
}
console.log('State actions', actions)

/** state definition */
const emptyFilter = {
  m_category: '',
  m_date: '',
  m_time: '',
  m_place: '',
  m_player: '',
  m_result: '',
  m_players: false,
  p_name: '',
  p_category: '',
  p_ranking: '',
  p_phone: '',
  p_email: '',
  p_junior: '-1',
  p_paid: '-1',
  p_matches: false,
}

export const state = {
  files: [],
  fileListStatus: 'uninitialized',
  calendars: [],
  playerList: null,
  calendarsStatus: 'uninitialized',
  playerLists: [],
  calendar: null,
  playerListsStatus: 'uninitialized',
  playerListDownloadStatus: 'uninitialized',
  playerListParseStatus: 'uninitialized',
  playerListDeleteStatus: 'uninitialized',
  calendarDownloadStatus: 'uninitialized',
  calendarParseStatus: 'uninitialized',
  calendarDeleteStatus: 'uninitialized',
  loginStatus: 'uninitialized',
  filteredMatches: [],
  filteredPlayers: [],
  filter: emptyFilter,
  stats: {},
  filteredStats: {},
}
console.log('State state', state)

/** reducer is called by the redux dispatcher and handles all component actions */
export function reducer(state = [], action) {
  let index
  switch (action.type) {
    case 'SWISSTENNIS/SET_FILTER':
      return { ...state, filter: { ...state.filter, ...action.data } }
    case 'SWISSTENNIS/FILTER_PLAYERS':
      return { ...state }
    case 'SWISSTENNIS/FILTER_PLAYERS_DONE':
      return { ...state, ...action.data }
    case 'SWISSTENNIS/FILTER_MATCHES':
      return { ...state }
    case 'SWISSTENNIS/FILTER_MATCHES_DONE':
      return { ...state, ...action.data }
    case 'SWISSTENNIS/FILE_LIST_REQUEST':
      return { ...state, fileListStatus: 'request' }
    case 'SWISSTENNIS/FILE_LIST_SUCCESS':
      return { ...state, fileListStatus: 'success', files: action.data }
    case 'SWISSTENNIS/FILE_LIST_FAILURE':
      return { ...state, fileListStatus: 'failure' }
    case 'SWISSTENNIS/LOGIN_REQUEST':
      return {
        ...state,
        loginStatus: 'request',
        playerListDownloadStatus: action.data.sequence
          ? 'uninitialized'
          : state.playerListDownloadStatus,
        playerListParseStatus: action.data.sequence
          ? 'uninitialized'
          : state.playerListParseStatus,
        calendarDownloadStatus: action.data.sequence
          ? 'uninitialized'
          : state.calendarDownloadStatus,
        calendarParseStatus: action.data.sequence
          ? 'uninitialized'
          : state.calendarParseStatus,
      }
    case 'SWISSTENNIS/LOGIN_SUCCESS':
      return { ...state, loginStatus: 'success' }
    case 'SWISSTENNIS/LOGIN_FAILURE':
      return { ...state, loginStatus: 'failure' }
    case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST':
      return { ...state, playerListDownloadStatus: 'request' }
    case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_SUCCESS':
      return {
        ...state,
        playerListDownloadStatus: 'success',
        files: [...state.files, action.data],
      }
    case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_FAILURE':
      return { ...state, playerListDownloadStatus: 'failure' }
    case 'SWISSTENNIS/PARSE_PLAYERLIST_REQUEST':
      return { ...state, playerListParseStatus: 'request' }
    case 'SWISSTENNIS/PARSE_PLAYERLIST_SUCCESS':
      return {
        ...state,
        playerListParseStatus: 'success',
        playerLists: [...state.playerLists, action.data.playerList],
      }
    case 'SWISSTENNIS/PARSE_PLAYERLIST_FAILURE':
      return { ...state, playerListParseStatus: 'failure' }
    case 'SWISSTENNIS/DELETE_PLAYERLIST_REQUEST':
      return { ...state, playerListDeleteStatus: 'request' }
    case 'SWISSTENNIS/DELETE_PLAYERLIST_SUCCESS':
      index = state.playerLists.findIndex(
        (playerList) => playerList._id === action.data._id
      )
      return {
        ...state,
        playerListDeleteStatus: 'success',
        playerLists: [
          ...state.playerLists.slice(0, index),
          ...state.playerLists.slice(index + 1),
        ],
      }
    case 'SWISSTENNIS/DELETE_PLAYERLIST_FAILURE':
      return { ...state, playerListDeleteStatus: 'failure' }
    case 'SWISSTENNIS/DOWNLOAD_CALENDAR_REQUEST':
      return { ...state, calendarDownloadStatus: 'request' }
    case 'SWISSTENNIS/DOWNLOAD_CALENDAR_SUCCESS':
      return {
        ...state,
        calendarDownloadStatus: 'success',
        files: [...state.files, action.data],
      }
    case 'SWISSTENNIS/DOWNLOAD_CALENDAR_FAILURE':
      return { ...state, calendarDownloadStatus: 'failure' }
    case 'SWISSTENNIS/PARSE_CALENDAR_REQUEST':
      return { ...state, calendarParseStatus: 'request' }
    case 'SWISSTENNIS/PARSE_CALENDAR_SUCCESS':
      return {
        ...state,
        calendarParseStatus: 'success',
        calendars: [...state.calendars, action.data.matchList],
      }
    case 'SWISSTENNIS/PARSE_CALENDAR_FAILURE':
      return { ...state, calendarParseStatus: 'failure' }
    case 'SWISSTENNIS/DELETE_CALENDAR_REQUEST':
      return { ...state, calendarDeleteStatus: 'request' }
    case 'SWISSTENNIS/DELETE_CALENDAR_SUCCESS':
      index = state.calendars.findIndex(
        (calendar) => calendar._id === action.data._id
      )
      return {
        ...state,
        calendarDeleteStatus: 'success',
        calendars: [
          ...state.calendars.slice(0, index),
          ...state.calendars.slice(index + 1),
        ],
      }
    case 'SWISSTENNIS/DELETE_CALENDAR_FAILURE':
      return { ...state, calendarDeleteStatus: 'failure' }
    case 'SWISSTENNIS/GET_CALENDAR_REQUEST':
      return { ...state, calendarsStatus: 'loading' }
    case 'SWISSTENNIS/GET_CALENDAR_SUCCESS':
      return { ...state, calendarsStatus: 'loaded', calendar: action.data }
    case 'SWISSTENNIS/GET_CALENDAR_FAILURE':
      return { ...state, calendarsStatus: 'failed' }
    case 'SWISSTENNIS/GET_CALENDARS_REQUEST':
      return { ...state, calendarsStatus: 'loading' }
    case 'SWISSTENNIS/GET_CALENDARS_SUCCESS':
      return { ...state, calendarsStatus: 'loaded', calendars: action.data }
    case 'SWISSTENNIS/GET_CALENDARS_FAILURE':
      return { ...state, calendarsStatus: 'failed' }
    case 'SWISSTENNIS/GET_PLAYERLIST_REQUEST':
      return { ...state, playerListsStatus: 'loading' }
    case 'SWISSTENNIS/GET_PLAYERLIST_SUCCESS':
      return { ...state, playerListsStatus: 'loaded', playerList: action.data }
    case 'SWISSTENNIS/GET_PLAYERLIST_FAILURE':
      return { ...state, playerListsStatus: 'failed' }
    case 'SWISSTENNIS/GET_PLAYERLISTS_REQUEST':
      return { ...state, playerListsStatus: 'loading' }
    case 'SWISSTENNIS/GET_PLAYERLISTS_SUCCESS':
      return { ...state, playerListsStatus: 'loaded', playerLists: action.data }
    case 'SWISSTENNIS/GET_PLAYERLISTS_FAILURE':
      return { ...state, playerListsStatus: 'failed' }
    default:
      return state
  }
}

function* filterPlayers(action) {
  const state = yield select()
  const { playerList } = state.swisstennis
  const filter = action.data
  console.log('filter', filter)
  const filteredPlayers = playerList
    ? playerList.players.filter((player) => {
        return (
          (filter.p_name
            ? player.fullName &&
              player.fullName
                .toLowerCase()
                .includes(filter.p_name.toLowerCase())
            : true) &&
          (filter.p_category
            ? player.category &&
              player.category
                .toLowerCase()
                .includes(filter.p_category.toLowerCase())
            : true) &&
          (filter.p_ranking
            ? player.ranking &&
              player.ranking
                .toLowerCase()
                .includes(filter.p_ranking.toLowerCase())
            : true) &&
          (filter.p_phone
            ? player.phone &&
              player.phone.toLowerCase().includes(filter.p_phone.toLowerCase())
            : true) &&
          (filter.p_email
            ? player.email &&
              player.email.toLowerCase().includes(filter.p_email.toLowerCase())
            : true) &&
          (filter.p_junior === '-1'
            ? true
            : player.junior === (filter.p_junior === '1')) &&
          (filter.p_paid === '-1'
            ? true
            : player.paid === (filter.p_paid === '1'))
        )
      })
    : []
  yield put(actions.filterPlayersDone({ filteredPlayers }))
}

function* filterMatches(action) {
  const state = yield select()
  const { calendar } = state.swisstennis
  const filter = action.data
  const filteredMatches = calendar
    ? calendar.matches.filter((match) => {
        return (
          (filter.m_category
            ? match.category &&
              match.category
                .toLowerCase()
                .includes(filter.m_category.toLowerCase())
            : true) &&
          (filter.m_place
            ? match.place &&
              match.place.toLowerCase().includes(filter.m_place.toLowerCase())
            : true) &&
          (filter.m_date
            ? match.date &&
              moment(match.date).format('DD.MM.YYYY').includes(filter.m_date)
            : true) &&
          (filter.m_time
            ? match.date &&
              moment(match.date).format('HH:mm').includes(filter.m_time)
            : true) &&
          (filter.m_player
            ? (match.player1 &&
                match.player1.fullName
                  .toLowerCase()
                  .includes(filter.m_player.toLowerCase())) ||
              (match.player2 &&
                match.player2.fullName
                  .toLowerCase()
                  .includes(filter.m_player.toLowerCase()))
            : true) &&
          (filter.m_result
            ? match.result.toLowerCase().includes(filter.m_result)
            : true)
        )
      })
    : []
  yield put(actions.filterMatchesDone({ filteredMatches }))
}

function* fileList(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/files`,
      method: 'GET',
      onSuccess: actions.fileListSuccess,
      onFailure: actions.fileListFailure,
    })
  )
}

function* login(action) {
  try {
    const token = localStorage.getItem('accessToken')
    console.log('Swisstennis login requested', action, token)
    const response = yield call(fetch, `${SZTM_API}/api/swisstennis/login`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-access-token': token,
      },
    })
    if (response.status !== 200) {
      console.log(response)
      throw Error(response.status)
    } else {
      const responseJson = yield response.json()
      yield put(actions.loginSuccess(responseJson))
      if (action.data.sequence)
        yield put(actions.downloadPlayerListRequest({ sequence: true }))
    }
  } catch (error) {
    console.log('Config failure!', actions.loginFailure(error))
    yield put(actions.loginFailure(error))
  }
}

function* getCalendars(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/calendars`,
      method: 'GET',
      onSuccess: actions.getCalendarsSuccess,
      onFailure: actions.getCalendarsFailure,
    })
  )
}

function* getCalendar(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/calendar${
        action.data && action.data.calendarId
          ? `?calendarId=${action.data.calendarId}`
          : ''
      }`,
      method: 'GET',
      onSuccess: [actions.getCalendarSuccess, actions.filterMatches],
      onFailure: actions.getCalendarFailure,
    })
  )
}

function* downloadCalendar(action) {
  try {
    const token = localStorage.getItem('accessToken')
    console.log('Download calendar requested', action, token)
    const response = yield call(
      fetch,
      `${SZTM_API}/api/swisstennis/calendar/download`,
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          'x-access-token': token,
        },
      }
    )
    if (response.status !== 200) {
      throw Error(response.status)
    } else {
      const responseJson = yield response.json()
      yield put(actions.downloadCalendarSuccess(responseJson))
      if (action.data.sequence)
        yield put(
          actions.parseCalendarRequest({
            filename: responseJson.filename,
            sequence: true,
          })
        )
    }
  } catch (error) {
    console.log('Config failure!', actions.downloadCalendarFailure(error))
    yield put(actions.downloadCalendarFailure(error))
  }
}

function* parseCalendar(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/calendar/parse/${action.data.filename}`,
      method: 'GET',
      onSuccess: actions.parseCalendarSuccess,
      onFailure: actions.parseCalendarFailure,
    })
  )
}

function* deleteCalendar(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/calendar/${action.data.calendarId}`,
      method: 'DELETE',
      onSuccess: actions.deleteCalendarSuccess,
      onFailure: actions.deleteCalendarFailure,
    })
  )
}

function* getPlayerLists(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/playerlists`,
      method: 'GET',
      onSuccess: actions.getPlayerListsSuccess,
      onFailure: actions.getPlayerListsFailure,
    })
  )
}

function* getPlayerList(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/playerlist${
        action.data && action.data.playerlistId
          ? `?playerlistId=${action.data.playerlistId}`
          : ''
      }`,
      method: 'GET',
      onSuccess: [actions.getPlayerListSuccess, actions.filterPlayers],
      onFailure: actions.getPlayerListFailure,
    })
  )
}

function* downloadPlayerList(action) {
  try {
    const token = localStorage.getItem('accessToken')
    console.log('Playerlist download requested', action, token)
    const response = yield call(
      fetch,
      `${SZTM_API}/api/swisstennis/playerlist/download`,
      {
        method: 'GET',
        headers: {
          'x-access-token': token,
        },
      }
    )
    if (response.status !== 200) {
      throw Error(response.status)
    } else {
      const responseJson = yield response.json()
      yield put(actions.downloadPlayerListSuccess(responseJson))
      if (action.data.sequence)
        yield put(
          actions.parsePlayerListRequest({
            filename: responseJson.filename,
            sequence: true,
          })
        )
    }
  } catch (error) {
    console.log('Config failure!', actions.downloadPlayerListFailure(error))
    yield put(actions.downloadPlayerListFailure(error))
  }
}

function* parsePlayerList(action) {
  try {
    const token = localStorage.getItem('accessToken')
    console.log('PlayerList parse requested', action, token)
    const response = yield call(
      fetch,
      `${SZTM_API}/api/swisstennis/playerlist/parse/${action.data.filename}`,
      {
        method: 'GET',
        headers: {
          'x-access-token': token,
        },
      }
    )
    if (response.status !== 200) {
      throw Error(response.status)
    } else {
      const responseJson = yield response.json()
      yield put(actions.parsePlayerListSuccess(responseJson))
      if (action.data.sequence)
        yield put(actions.downloadCalendarRequest({ sequence: true }))
    }
  } catch (error) {
    console.log('Config failure!', actions.parsePlayerListFailure(error))
    yield put(actions.parsePlayerListFailure(error))
  }
}

function* deletePlayerList(action) {
  yield put(
    api.actions.apiRequest({
      path: `api/swisstennis/playerlist/${action.data.playerlistId}`,
      method: 'DELETE',
      onSuccess: actions.deletePlayerListSuccess,
      onFailure: actions.deletePlayerListFailure,
    })
  )
}

/** sagas are asynchronous workers (JS generators) to handle the state. */
export function* saga() {
  console.log('Config saga started.')
  yield takeEvery('SWISSTENNIS/FILE_LIST_REQUEST', fileList)
  yield takeEvery('SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST', downloadPlayerList)
  yield takeEvery('SWISSTENNIS/PARSE_PLAYERLIST_REQUEST', parsePlayerList)
  yield takeEvery('SWISSTENNIS/DELETE_PLAYERLIST_REQUEST', deletePlayerList)
  yield takeEvery('SWISSTENNIS/DOWNLOAD_CALENDAR_REQUEST', downloadCalendar)
  yield takeEvery('SWISSTENNIS/PARSE_CALENDAR_REQUEST', parseCalendar)
  yield takeEvery('SWISSTENNIS/DELETE_CALENDAR_REQUEST', deleteCalendar)
  yield takeEvery('SWISSTENNIS/GET_CALENDAR_REQUEST', getCalendar)
  yield takeEvery('SWISSTENNIS/GET_CALENDARS_REQUEST', getCalendars)
  yield takeEvery('SWISSTENNIS/GET_PLAYERLIST_REQUEST', getPlayerList)
  yield takeEvery('SWISSTENNIS/GET_PLAYERLISTS_REQUEST', getPlayerLists)
  yield takeEvery('SWISSTENNIS/LOGIN_REQUEST', login)
  yield takeEvery('SWISSTENNIS/FILTER_PLAYERS', filterPlayers)
  yield takeEvery('SWISSTENNIS/FILTER_MATCHES', filterMatches)
}
