import axios from 'axios'
import { APIError, callOrReturn, handleApiError } from '../api-helper'

const cloneMinimalShowObject = function (show) {
    /* returns a new minimal object from the current show object with all
    properties needed for a PUT request to the /show/ endpoint */
    let s = {}
    s.name = show.name
    s.slug = show.slug
    s.short_description = show.short_description
    s.fundingcategory = show.fundingcategory
    s.type = show.type
    // we do not want the arrays do be passed as references, because the
    // current show object should not get modified when the update object
    // gets modified, therefore we use slice to clone the arrays
    s.category = show.category.slice()
    s.hosts = show.hosts.slice()
    s.owners = show.owners.slice()
    s.language = show.language.slice()
    s.topic = show.topic.slice()
    s.musicfocus = show.musicfocus.slice()
    return s
}

const state = {
    shows: [],
    schedule: null,
    schedules: [],
    scheduleTimeslots: [],
    timeslots: [],
    notes: [],
    types: [],
    fundingcategories: [],
    categories: [],
    topics: [],
    musicfocus: [],
    languages: [],
    hosts: [],
    loaded: {
        shows: false,
        timeslots: false,
        notes: false,
        schedule: false,
        scheduleTimeslots: false,
        schedules: false,
        types: false,
        fundingcategories: false,
        categories: false,
        topics: false,
        musicfocus: false,
        languages: false,
        hosts: false,
    },
    selected: {
        index: 0, // index of the currently selected show in our shows array
        id: 0,    // actual id of the currently selected show
    }
}

const getters = {
    shows: state => state.shows,
    selectedShow: state => state.shows[state.selected.index],
    schedule: state => state.schedule,
    schedules: state => state.schedules,
    scheduleTimeslots: state => state.scheduleTimeslots,
    timeslots: state => state.timeslots,
    notes: state => state.notes,
    types: state => state.types,
    fundingcategories: state => state.fundingcategories,
    categories: state => state.categories,
    topics: state => state.topics,
    musicfocus: state => state.musicfocus,
    languages: state => state.languages,
    hosts: state => state.hosts,
    getShowByDataParam: state => data => {
        let show
        if (data.id !== undefined) {
            show = state.shows.find(s => s.id === data.id)
            if (show === undefined) {
                console.log('[ERROR] getShowByDataParam: ID not found in store!')
            }
        } else if (data.index !== undefined) {
            show = state.shows[data.index]
        } else {
            console.log('[ERROR] getShowByDataParam: no ID or index was provided')
        }
        return show
    },
    getTimeslotById: state => id => {
        return state.timeslots.find(s => s.id === id)
    }
}

const mutations = {
    loading(state, item) {
        state.loaded[item] = false
    },
    finishLoading(state, item) {
        state.loaded[item] = true
    },

    setShows(state, shows) {
        state.shows = shows
    },
    addShow(state, show) {
        state.shows.push(show)
        state.shows.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase())
    },

    setSchedule(state, schedule) {
        state.schedule = schedule
    },
    setSchedules(state, schedules) {
        state.schedules = schedules
    },
    setScheduleTimeslots(state, slots) {
        state.scheduleTimeslots = slots
    },

    setTimeslots(state, slots) {
        state.timeslots = slots
    },
    setTimeslot(state, slot) {
        let index = state.timeslots.findIndex(s => s.id === slot.id)
        state.timeslots.splice(index, 1, slot)
    },

    setNotes(state, notes) {
        state.notes = notes
    },
    addNote(state, note) {
        state.notes.push(note)
    },
    setNote(state, note) {
        let index = state.notes.findIndex(n => n.id === note.id)
        state.notes.splice(index, 1, note)
    },

    setName(state, data) {
        let index = state.shows.findIndex(s => s.id === data.id)
        state.shows[index].name = data.text
    },
    setShortDescription(state, data) {
        let index = state.shows.findIndex(s => s.id === data.id)
        state.shows[index].short_description = data.text
    },
    setDescription(state, data) {
        let index = state.shows.findIndex(s => s.id === data.id)
        state.shows[index].description = data.text
    },
    setActive(state, data) {
        let index = state.shows.findIndex(s => s.id === data.id)
        state.shows[index].is_active = data.active
    },
    setProperty(state, data) {
        let index = state.shows.findIndex(s => s.id === data.id)
        state.shows[index][data.property] = data.value
    },

    setMetaArray(state, data) {
        state[data.property] = data.value
    },

    switchShow(state, index) {
        if (state.loaded.shows) {
            state.selected.index = index
            state.selected.id = state.shows[index].id
        }
    },
    switchShowById(state, id) {
        if (state.loaded.shows) {
            state.selected.index = state.shows.findIndex(s => s.id === id)
            state.selected.id = id
        }
    },
}

const actions = {
    fetchShows(ctx, data) {
        const user = ctx.rootState.auth.user.steeringUser
        ctx.commit('loading', 'shows')
        let uri = process.env.VUE_APP_API_STEERING + 'shows'
        // normal users should only see their own shows, only superusers see all shows
        if (user && !user.is_superuser) {
            uri += '?owner=' + ctx.rootState.auth.user.steeringUser.id
        }
        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setShows', response.data)
            ctx.commit('finishLoading', 'shows')
            if (data && typeof (data.callback) === 'function') {
                data.callback()
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load shows')
            console.log(error)
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    fetchSchedule(ctx, data) {
        ctx.commit('loading', 'schedule')
        let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show +
            '/schedules/' + data.schedule + '/'
        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setSchedule', response.data)
            ctx.commit('finishLoading', 'schedule')
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load schedule')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    fetchSchedules(ctx, data) {
        ctx.commit('loading', 'schedule')
        let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/'

        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setSchedules', response.data)
            ctx.commit('finishLoading', 'schedule')
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load schedule')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    fetchTimeslots(ctx, data) {
        if (data.schedule !== undefined) {
            ctx.commit('loading', 'scheduleTimeslots')
        } else {
            ctx.commit('loading', 'timeslots')
        }

        let uri
        if (data.id !== undefined) {
            uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.id

            if (data.schedule !== undefined) {
                uri += '/schedules/' + data.schedule + '/timeslots/?'
            } else {
                uri += '/timeslots/?'
            }

            if (data.surrounding) {
                uri += 'surrounding'
            }
        } else {
            uri = process.env.VUE_APP_API_STEERING + 'timeslots?'
        }

        if (!data.surrounding) {
            uri += 'start=' + data.start
            uri += '&end=' + data.end
            if (data.limit) {
                uri += '&limit=' + data.limit
            }
            if (data.offset) {
                uri += '&offset=' + data.offset
            }
        }

        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            if (data.schedule !== undefined) {
                ctx.commit('setScheduleTimeslots', response.data)
                ctx.commit('finishLoading', 'scheduleTimeslots')
            } else {
                if (data.limit) {
                    ctx.commit('setTimeslots', response.data.results)
                } else {
                    ctx.commit('setTimeslots', response.data)
                }
                ctx.commit('finishLoading', 'timeslots')
            }
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load timeslots')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    fetchNotes(ctx, data) {
        ctx.commit('loading', 'notes')
        // we only have to make an API call if there are actually any notes
        // otherwise the notes are just and empty array
        if (data.notes.length === 0) {
            ctx.commit('setNotes', [])
            ctx.commit('finishLoading', 'notes')
            return
        }
        let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/notes/?ids='
        for (let id of data.notes) {
            uri += id + ','
        }
        uri = uri.slice(0, -1) // now remove trailing ','
        this.$log.debug('fetchNotes: uri:', uri)
        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setNotes', response.data)
            ctx.commit('finishLoading', 'notes')
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load notes')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    fetchMetaArray(ctx, data) {
        ctx.commit('loading', data.property)
        let uri = process.env.VUE_APP_API_STEERING + data.property + '/'
        if (data.onlyActive === true) {
            uri += '?active=true'
        }
        axios.get(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setMetaArray', {property: data.property, value: response.data})
            ctx.commit('finishLoading', data.property)
            if (data && typeof (data.callback) === 'function') {
                data.callback()
            }
        }).catch(error => {
            handleApiError(this, error, 'could not load ' + data.property)
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    submitSchedule(ctx, data) {
        ctx.commit('loading', 'schedules')
        let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.showId + '/schedules/'
        return axios.post(uri, data.schedule, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('finishLoading', 'schedules')
            return callOrReturn(response, data?.callback)
        }).catch(error => {
            APIError.handle(error, 'Unable to submit schedule', data, this)
        })
    },

    submitShow(ctx, data) {
        let uri = process.env.VUE_APP_API_STEERING_SHOWS
        axios.post(uri, data.show, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('addShow', response.data)
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not add new show')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    submitNote(ctx, data) {
        const NON_UPDATEABLE_PROPERTIES = ['thumbnails', 'width', 'height']

        let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id +
            '/schedules/' + data.scheduleID +
            '/timeslots/' + data.timeslotID +
            '/note/'
        if (data.update) {
            uri += data.note.id + '/'
        }

        let method = data.update ? 'put' : 'post'
        let formData = new FormData()

        // We serialize the data to a FormData object, so we can send the image
        // as binary blob to the Steering API.
        for (const [key, value] of Object.entries(data.note)) {
            // When we're updating we don't want to include some properties that cause errors.
            if (data.update && NON_UPDATEABLE_PROPERTIES.includes(key)) {
                continue
            }

            formData.append(key, value)
        }

        axios.request({
            url: uri,
            method: method,
            data: formData,
            responseType: 'json',  // we need this explicitly here, as it does not seem to work automagically as in GET and PUT requests
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            if (data.update) {
                ctx.commit('setNote', response.data)
            } else {
                ctx.commit('addNote', response.data)
            }
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            let msg = data.update ? 'could not update note' : 'could not add new note'
            handleApiError(this, error, msg)
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    updateShow(ctx, data) {
        let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/'
        axios.put(uri, data.show, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel(error.response)
            }
        })
    },

    updateProperty(ctx, data) {
        let show = cloneMinimalShowObject(ctx.getters.getShowByDataParam(data))
        show[data.property] = data.value
        ctx.dispatch('updateShow', {
            id: data.id,
            show: show,
            callback: () => {
                ctx.commit('setProperty', {
                    id: data.id,
                    property: data.property,
                    value: data.value
                })

                if (typeof (data.callback) === 'function') {
                    data.callback()
                }
            },
            callbackCancel: error => {
                if (typeof (data.callbackCancel) === 'function') {
                    data.callbackCancel(error)
                }
            }
        })
    },

    updateImage(ctx, data) {
        let show = ctx.getters.getShowByDataParam(data)
        let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/'
        let formData = new FormData()
        // these propoerties have to be sent always (and they must not be null)
        formData.append('name', show.name)
        formData.append('slug', show.slug)
        formData.append('short_description', show.short_description)
        formData.append('type', show.type)
        formData.append('fundingcategory', show.fundingcategory)
        formData.append('is_active', show.is_active)
        // now we append the new logo/image file (signified by data.type)
        formData.append(data.type, data.file, data.file.name)
        axios.put(uri, formData, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(response => {
            ctx.commit('setProperty', {
                id: data.id,
                property: data.type,
                // when updating images of a show the steering API does not return
                // the full URI but only the path on the server
                // TODO: create an issue in steering, to create consistency
                value: process.env.VUE_APP_BASEURI_STEERING + response.data[data.type]
            })
            if (data && typeof (data.callback) === 'function') {
                data.callback(response)
            }
        }).catch(error => {
            handleApiError(this, error, 'could not update show ' + data.type)
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    updateTimeslot(ctx, data) {
        let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show +
            '/schedules/' + data.schedule +
            '/timeslots/' + data.timeslot.id + '/'
        axios.put(uri, data.timeslot, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(() => {
            ctx.commit('setTimeslot', data.timeslot)
            if (data && typeof (data.callback) === 'function') {
                data.callback()
            }
        }).catch(error => {
            handleApiError(this, error, 'could not update timeslot')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })

    },

    deleteSchedule(ctx, data) {
        let uri = process.env.VUE_APP_API_STEERING +
            'shows/' + data.show +
            '/schedules/' + data.schedule + '/'
        axios.delete(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(() => {
            if (data && typeof (data.callback) === 'function') {
                data.callback()
            }
        }).catch(error => {
            handleApiError(this, error, 'could not delete full schedule')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

    deleteTimeslot(ctx, data) {
        let uri = process.env.VUE_APP_API_STEERING +
            'shows/' + data.show +
            '/schedules/' + data.schedule +
            '/timeslots/' + data.timeslot + '/'
        axios.delete(uri, {
            headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token}
        }).then(() => {
            if (data && typeof (data.callback) === 'function') {
                data.callback()
            }
        }).catch(error => {
            handleApiError(this, error, 'could not delete single timeslot')
            if (data && typeof (data.callbackCancel) === 'function') {
                data.callbackCancel()
            }
        })
    },

}

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations,
}