import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { CategoryType } from '@shared/types/category.type'
import { ThunkDTO, ThunkPersist } from '@shared/types/thunk.type'
import { RoutineManager } from '@utils/routine.manager'
import { RootState } from '../../stores'
import { startListening } from '../../stores/middleware/listener.middleware'
import { setToken } from './user.reducer'

export interface ReducerState {
    thunks: Record<CategoryType, ThunkPersist | null>
}

const initialState: ReducerState = {
    thunks: {
        DEVICE: null,
        TABLE: null,
        TAB: null
    },

}


const RoutineSlice = createSlice({
    name: 'routine',
    initialState,
    reducers: {
        addRoutine: (state: any, { payload }: PayloadAction<ThunkDTO>) => {
            const thunk: ThunkPersist = {
                category: payload.category,
                payload: payload.payload,
                thunkId: payload.thunk.id,
                lastupdate: Date.now()
            }

            state.thunks[payload.category] = thunk
        },

        refreshUpdateTimer: (state: any, { payload }: PayloadAction<CategoryType>) => {
            state.thunks[payload].lastupdate = Date.now()
        },

        removeAll: (state) => {
            state.thunks = initialState.thunks
        },
    },
})

export const { addRoutine, refreshUpdateTimer } = RoutineSlice.actions

// on logout remove all routine
startListening({
    predicate: (action) => setToken.match(action) && action.payload === null,
    effect: async (_, listenerApi) => {
        RoutineManager.unsubscribeAll()
        listenerApi.dispatch(RoutineSlice.actions.removeAll())
    },
})

/**
 * Add new routine
 * 
 * This listener handles the connection between the RoutingManager that 
 * stores the non persistable thunk object and the peristable thunk information.
 * The persistable information are stored in this reducer
 */
startListening({
    predicate: (action) => addRoutine.match(action),
    effect: async (action, listenerApi) => {
        const { thunk } = action.payload as ThunkDTO
        const subscription = await listenerApi.dispatch(thunk(action.payload.payload))

        if (subscription.error) {
            throw new Error('Error during routine execution: ' + subscription.error.message)
        }

        RoutineManager.add(subscription.payload, action.payload.category)
    },
})

// unsubscribe old routine that is in the same category
startListening({
    predicate: (action) => addRoutine.match(action),
    effect: async (action, listenerApi) => {
        const { routine } = listenerApi.getOriginalState() as RootState
        const category = action.payload.category;

        const lastThunk = routine.thunks[category as CategoryType]
        if (lastThunk) {
            RoutineManager.unsubscribe(category)
        }
    },
})

export default RoutineSlice.reducer