import deepmerge from 'deepmerge'
import { AnyAction, combineReducers, Middleware, Reducer } from 'redux'
import { combineEpics, createEpicMiddleware, Epic } from 'redux-observable'

// import { sessionEpic } from '@agiliza/epics'
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'

import entitiesReducer, { EntitiesState, initialState as entitiesInitState } from './entities'
import { errorMiddleware, printMiddleware } from './middlewares'
import {
  initialState as sessionInitState,
  reducer as sessionReducer,
  State as SessionState
} from './session'
import uiReducer, { initialState as uiInitState, UIState } from './ui'
import useCases, { initialState as useCasesInitState, UseCasesState } from './useCases'

type ReducerMap = Record<string, Reducer>
type Reducers = Record<string, Reducer | ReducerMap>

let registeredReducers: Reducers = {
  entities: entitiesReducer,
  session: sessionReducer,
  ui: uiReducer,
  useCases,
}

function recombineReducers(reducers: Reducers) {
  const result = Object.keys(reducers).reduce((acc, k) => {
    const reducer = reducers[k]
    acc[k] = typeof reducer === 'object' ? combineReducers(reducer) : reducer
    return acc
  }, {} as ReducerMap)
  return combineReducers(result)
}

let combinedReducers = recombineReducers(registeredReducers)

const rootReducer = (state, action: AnyAction) => {
  if (action.type === 'RESET_STORE') {
    state = undefined
  }

  return combinedReducers(state, action)
}

const defaultMiddleware = getDefaultMiddleware({
  serializableCheck: false,
})

const middlewares = !process.env.PRODUCTION ? [/*createLogger({ collapsed: true }), */ errorMiddleware, printMiddleware] : [errorMiddleware]

const epicMiddleware = createEpicMiddleware()

const store = configureStore({
  reducer: rootReducer,
  middleware: [...defaultMiddleware, ...middlewares, epicMiddleware] as Middleware[],
  preloadedState: {
    session: sessionInitState,
    ui: uiInitState,
    entities: entitiesInitState,
    useCases: useCasesInitState,
  },
  devTools: process.env.NODE_ENV === 'development',
})

type EpicMap = Record<string, Epic>
type Epics = Record<string, Epic | EpicMap>

let registeredEpics: Epics = {
  // session: sessionEpic,
}

function recombineEpics(epics: Epics) {
  const result = Object.keys(epics).reduce((acc, k) => {
    const epic = epics[k]
    acc[k] = typeof epic === 'object' ? combineEpics(...Object.values(epic)) : epic
    return acc
  }, {} as EpicMap)
  return combineEpics(...Object.values(result))
}

let combinedEpics = recombineEpics(registeredEpics)
epicMiddleware.run(combinedEpics)

function updateStore(newReducers: Reducers, newEpics: Epics) {
  registeredReducers = deepmerge(registeredReducers, newReducers)
  registeredEpics = deepmerge(registeredEpics, newEpics)

  combinedReducers = recombineReducers(registeredReducers)
  combinedEpics = recombineEpics(registeredEpics)

  store.replaceReducer((state, action: AnyAction) => {
    if (action.type === 'RESET_STORE') {
      state = undefined
    }

    return combinedReducers(state, action)
  })

  epicMiddleware.run(combinedEpics)
}

export interface StoreState {
  ui: UIState
  session: SessionState
  entities: EntitiesState
  useCases: UseCasesState
}

const wd: any = window
wd.__CURIO_REGISTER_VIEW__ = updateStore

export { store }
