/** @module users/state */
import { call, put, takeEvery, select, all } from 'redux-saga/effects'
import { replace } from 'connected-react-router'
import jwt from 'jwt-simple'
import { SZTM_API } from '../local-constants'

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

/** actionTypes define what actions are handeled by the reducer. */
export const actions = {
    apiRequest: (data) => {
        return {
            type: 'API/REQUEST',
            data
        }
    },
    apiSuccess: (data) => {
        return {
            type: 'API/SUCCESS',
            data
        }
    },
    apiFailure: (error) => {
        return {
            type: 'API/FAILURE',
            error
        }
    },
    apiAuthenticationExpired: () => {
        return {
            type: 'API/AUTHENTICATION_EXPIRED'
        }
    },
}
console.log('State actions', actions)

/** state definition */

// Check, if there is a valid token.
function validateToken (token) {
    console.log(token)
    const tokenData = token && jwt.decode(token, null, true)
    const tokenValid = tokenData && tokenData.expires > Date.now()
    return { token, tokenData, tokenValid }
}
const { token, tokenData, tokenValid } = validateToken(localStorage.getItem('accessToken'))

export const state = {
    loginStatus: 'uninitialized',
    token,
    tokenData,
    tokenValid
}
console.log('State state', state)

/** reducer is called by the redux dispatcher and handles all component actions */
export function reducer (state = [], action) {
    switch (action.type) {
        case 'API/SUCCESS':
            return { ...state, ...action.data }
        case 'API/FAILURE':
            return { ...state, loginRequested: false }
        case 'API/AUTHENTICATION_EXPIRED':
            return { ...state, tokenValid: false }
        default:
            return state
    }
}

export function * login (action) {
    try {
        const state = yield select()
        console.log('User login requested', state, action) 
        const response = yield call(fetch, `${SZTM_API}/authenticate/login`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(action.data)
        })
        const responseJson = yield response.json()
        if (response.status !== 200) {
            throw new Error(responseJson)
        }
        const { token, tokenData, tokenValid } = validateToken(responseJson)
        localStorage.setItem('accessToken', token)
        console.log('User login success!', token, tokenData)
        yield put(actions.loginSuccess({token, tokenData, tokenValid}))
        if (state.router.location.originalAction) yield put(state.router.location.originalAction)
        if (state.router.location.originalPath) {
            yield put(replace({pathname: state.router.location.originalPath}))
        } else {
            yield put(replace({pathname: '/swisstennis'}))
        }
    } catch (error) {
        console.log('User login failure!', error)
        yield put(actions.loginFailure(error))
    }
}

export function * api (action) {
    const state = yield select()
    console.log('API requested', action, 'state', state, 'token', token)
    const { path, method, headers, body, params, query, onSuccess, onFailure } = action.data
    if (!state.api.tokenValues || state.api.tokenValues.expires < new Date()) yield put(replace({
        path: '/login',
        state: {}
    }))
    const paramsString = params ? '/' + Object.keys(params).map(key => encodeURIComponent(params[key])).join('/') : ''
    const queryString = query ? '?' + Object.keys(query).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`).join('&') : ''

    const response = yield call(fetch, `${SZTM_API}/${path}${paramsString}${queryString}`, {
        method: method || 'GET',
        headers: {
            'Content-Type': 'application/json',
            'x-access-token': token,
            headers
        },
        body
    })
    console.log('API received response', response) 
    if (response.status === 200) {
        if (onSuccess instanceof Array) {
            console.log('array of functions.')
            const responseJson = yield response.json()
            yield all(onSuccess.map((fn) => {
                console.log('function', fn)
                return put(fn(responseJson))
            }))
        } else {
            return yield put(onSuccess(yield response.json()))
        }
    } else if (response.status === 403) {
        yield put(onFailure(yield response.json()))
        return yield put(replace({pathname: '/login', originalAction: action, originalPath: state.router.location.pathname}))
    } else {
        return yield put(onFailure(yield response.text()))
    }
}

/** sagas are asynchronous workers (JS generators) to handle the state. */
export function * saga () {
    console.log('User saga started.')
    yield takeEvery('API/REQUEST', api)
    yield takeEvery('API/LOGIN', login)
}
