/*
 * <<
 * Davinci
 * ==
 * Copyright (C) 2016 - 2017 EDP
 * ==
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * >>
 */

import produce from 'immer'

import { ActionTypes } from './constants'
import { VizActionType } from './actions'
import { IVizState, ISlideFormed } from './types'
import { LOCATION_CHANGE, LocationChangeAction } from 'connected-react-router'
import { matchPortalPath, matchDisplaySlidePath } from 'utils/router'

const initialState: IVizState = {
  portals: [],
  displays: [],
  portalDashboards: {},
  displaySlides: {},
  currentPortalId: 0,
  currentDisplay: null,
  currentSlide: null,
  loading: {
    portal: false,
    display: false,
    editing: false,
    dashboards: false,
    slides: false
  }
}

const vizReducer = (
  state = initialState,
  action: VizActionType | LocationChangeAction
) =>
  produce(state, (draft: IVizState) => {
    let displayId: number
    let slide: ISlideFormed
    let slides: ISlideFormed[]
    let slideId: number

    switch (action.type) {
      case ActionTypes.LOAD_PORTALS:
        draft.loading.portal = true
        break
      case ActionTypes.LOAD_PORTALS_SUCCESS:
        draft.portals = action.payload.result
        draft.loading.portal = false
        break
      case ActionTypes.LOAD_PORTALS_FAILURE:
        draft.loading.portal = false
        break

      case ActionTypes.ADD_PORTAL:
        draft.loading.editing = true
        break
      case ActionTypes.ADD_PORTAL_SUCCESS:
        draft.portals.unshift(action.payload.result)
        draft.loading.editing = false
        break
      case ActionTypes.ADD_PORTAL_FAILURE:
        draft.loading.editing = false
        break

      case ActionTypes.DELETE_PORTAL:
        break
      case ActionTypes.DELETE_PORTAL_SUCCESS:
        draft.portals = draft.portals.filter((g) => g.id !== action.payload.id)
        break
      case ActionTypes.DELETE_PORTAL_FAILURE:
        break

      case ActionTypes.EDIT_PORTAL:
        draft.loading.editing = true
        break
      case ActionTypes.EDIT_PORTAL_SUCCESS:
        draft.portals.splice(
          draft.portals.findIndex((g) => g.id === action.payload.result.id),
          1,
          action.payload.result
        )
        draft.loading.editing = false
        break
      case ActionTypes.EDIT_PORTAL_FAILURE:
        draft.loading.editing = true
        break

      case ActionTypes.LOAD_PORTAL_DASHBOARDS:
        draft.loading.dashboards = true
        break
      case ActionTypes.LOAD_PORTAL_DASHBOARDS_SUCCESS:
        draft.portalDashboards[action.payload.portalId] =
          action.payload.dashboards
        draft.loading.dashboards = false
        if (!draft.currentPortalId) {
          draft.currentPortalId = action.payload.portalId
        }
        break
      case ActionTypes.LOAD_PORTAL_DASHBOARDS_FAILURE:
        delete draft.portalDashboards[action.payload.portalId]
        draft.loading.dashboards = false
        break

      case ActionTypes.ADD_DASHBOARD:
        draft.loading.editing = true
        break
      case ActionTypes.ADD_DASHBOARD_SUCCESS:
        draft.portalDashboards[action.payload.result.dashboardPortalId].push(
          action.payload.result
        )
        draft.loading.editing = false
        break
      case ActionTypes.ADD_DASHBOARD_FAILURE:
        draft.loading.editing = false
        break
      case ActionTypes.EDIT_DASHBOARD:
        draft.loading.editing = true
        break
      case ActionTypes.EDIT_DASHBOARD_SUCCESS:
        const { result, formType } = action.payload
        if (formType === 'edit') {
          result.forEach((r) => {
            draft.portalDashboards[r.dashboardPortalId].splice(
              draft.portalDashboards[r.dashboardPortalId].findIndex(
                (d) => d.id === r.id
              ),
              1,
              r
            )
          })
        } else if (formType === 'move') {
          draft.portalDashboards[
            result[0].dashboardPortalId
          ] = draft.portalDashboards[result[0].dashboardPortalId].filter(
            (d) => result.findIndex(({ id }) => id === d.id) < 0
          )
          draft.portalDashboards[
            result[0].dashboardPortalId
          ] = draft.portalDashboards[result[0].dashboardPortalId].concat(result)
        }
        draft.loading.editing = false
        break
      case ActionTypes.EDIT_DASHBOARD_FAILURE:
        draft.loading.editing = false
        break
      case ActionTypes.DELETE_DASHBOARD_SUCCESS:
        draft.portalDashboards[
          action.payload.portalId
        ] = draft.portalDashboards[action.payload.portalId].filter(
          ({ id }) => id !== action.payload.id
        )
        break

      case ActionTypes.LOAD_DISPLAYS:
        draft.loading.display = true
        break
      case ActionTypes.LOAD_DISPLAYS_SUCCESS:
        draft.displays = action.payload.displays
        draft.loading.display = false
        break
      case ActionTypes.LOAD_DISPLAYS_FAILURE:
        draft.loading.display = false
        break

      case ActionTypes.ADD_DISPLAY:
      case ActionTypes.EDIT_DISPLAY:
      case ActionTypes.COPY_DISPLAY:
        draft.loading.editing = true
        break
      case ActionTypes.ADD_DISPLAY_SUCCESS:
        draft.displays.unshift(action.payload.result)
        draft.loading.editing = false
        break
      case ActionTypes.EDIT_DISPLAY_SUCCESS:
        draft.displays.splice(
          draft.displays.findIndex(({ id }) => id === action.payload.result.id),
          1,
          action.payload.result
        )
        if (
          draft.currentDisplay &&
          action.payload.result.id === draft.currentDisplay.id
        ) {
          draft.currentDisplay = action.payload.result
        }
        draft.loading.editing = false
        break
      case ActionTypes.COPY_DISPLAY_SUCCESS:
        draft.displays.unshift(action.payload.display)
        break

      case ActionTypes.ADD_DISPLAY_FAILURE:
      case ActionTypes.EDIT_DISPLAY_FAILURE:
      case ActionTypes.COPY_DISPLAY_FAILURE:
        draft.loading.editing = false
        break

      case ActionTypes.DELETE_DISPLAY_SUCCESS:
        draft.displays = draft.displays.filter(
          (d) => d.id !== action.payload.id
        )
        break
      case ActionTypes.DELETE_DISPLAY_FAILURE:
        break

      case ActionTypes.UPDATE_CURRENT_DISPLAY:
        draft.currentDisplay = action.payload.display
        break

      case ActionTypes.LOAD_DISPLAY_SLIDES:
        draft.loading.slides = true
        break
      case ActionTypes.LOAD_DISPLAY_SLIDES_SUCCESS:
        draft.displaySlides[action.payload.display.id] = action.payload.slides
        draft.loading.slides = false
        break
      case ActionTypes.LOAD_DISPLAY_SLIDES_FAILURE:
        delete draft.displaySlides[action.payload.displayId]
        draft.loading.slides = false
        break

      case ActionTypes.ADD_SLIDE_SUCCESS:
        slide = action.payload.slide
        const { insertIdx, afterSlides } = action.payload
        displayId = slide.displayId
        draft.displaySlides[displayId].splice(insertIdx)
        draft.displaySlides[displayId].push(slide)
        draft.displaySlides[displayId] = draft.displaySlides[displayId].concat(
          afterSlides
        )
        break
      case ActionTypes.EDIT_SLIDES_SUCCESS:
        slides = action.payload.slides
        displayId = action.payload.displayId
        const editedSlideIds = slides.map(({ id }) => id)
        draft.displaySlides[displayId] = draft.displaySlides[displayId]
          .filter(({ id }) => !editedSlideIds.includes(id))
          .concat(slides)
          .sort((s1, s2) => s1.index - s2.index)
        if (editedSlideIds.includes(draft.currentSlide.id)) {
          draft.currentSlide = slides.find(
            ({ id }) => id === draft.currentSlide.id
          )
        }
        break
      case ActionTypes.DELETE_SLIDES_SUCCESS:
        displayId = action.payload.displayId
        action.payload.slideIds.forEach((deleteId) => {
          draft.displaySlides[displayId].splice(
            draft.displaySlides[displayId].findIndex(
              ({ id }) => id === deleteId
            ),
            1
          )
        })
        break

      case LOCATION_CHANGE:
        const matchPortal = matchPortalPath(action.payload.location.pathname)
        if (matchPortal) {
          draft.currentPortalId = +matchPortal.params.portalId || 0
          break
        }
        const matchDisplaySlide = matchDisplaySlidePath(
          action.payload.location.pathname
        )
        if (matchDisplaySlide) {
          const nextDisplayId = +matchDisplaySlide.params.displayId
          const nextSlides = draft.displaySlides[nextDisplayId]
          if (nextSlides) {
            const nextSlideId = +matchDisplaySlide.params.slideId
            const nextSlide = nextSlides.find(({ id }) => id === nextSlideId)
            draft.currentSlide = nextSlide
          } else {
            draft.currentSlide = null
          }
        } else {
          draft.currentDisplay = null
          draft.currentSlide = null
        }
        break
    }
  })

export { initialState as vizInitialState}
export default vizReducer
