import { fetchApi } from "../utils"
import reduceStore, * as storeActions from "./utils/store"
import Filt from "../filt/filt"
import { RequestUtils } from "../flow/utils"

export const ADD            = 'FLOWS_ADD'
export const UPDATE         = 'FLOWS_UPDATE'
export const REMOVE         = 'FLOWS_REMOVE'
export const RECEIVE        = 'FLOWS_RECEIVE'
export const SELECT         = 'FLOWS_SELECT'
export const SET_FILTER     = 'FLOWS_SET_FILTER'
export const SET_SORT       = 'FLOWS_SET_SORT'
export const SET_HIGHLIGHT  = 'FLOWS_SET_HIGHLIGHT'
export const REQUEST_ACTION = 'FLOWS_REQUEST_ACTION'


const defaultState = {
    highlight: null,
    filter: null,
    sort: { column: null, desc: false },
    selected: [],
    ...reduceStore(undefined, {})
}

export default function reduce(state = defaultState, action) {
    switch (action.type) {

        case ADD:
        case UPDATE:
        case REMOVE:
        case RECEIVE:
            // FIXME: Update state.selected on REMOVE:
            // The selected flow may have been removed, we need to select the next one in the view.
            let storeAction = storeActions[action.cmd](
                action.data,
                makeFilter(state.filter),
                makeSort(state.sort)
            )

            let selected = state.selected
            if(action.type === REMOVE && state.selected.includes(action.data)) {
                if(state.selected.length > 1){
                    selected = selected.filter(x => x !== action.data)
                } else {
                    selected = []
                    if (action.data in state.viewIndex && state.view.length > 1) {
                        let currentIndex = state.viewIndex[action.data],
                            nextSelection
                        if(currentIndex === state.view.length -1){ // last row
                            nextSelection = state.view[currentIndex - 1]
                        } else {
                            nextSelection = state.view[currentIndex + 1]
                        }
                        selected.push(nextSelection.id)
                    }
                }
            }

            return {
                ...state,
                selected,
                ...reduceStore(state, storeAction)
            }

        case SET_FILTER:
            return {
                ...state,
                filter: action.filter,
                ...reduceStore(state, storeActions.setFilter(makeFilter(action.filter), makeSort(state.sort)))
            }

        case SET_HIGHLIGHT:
            return {
                ...state,
                highlight: action.highlight
            }

        case SET_SORT:
            return {
                ...state,
                sort: action.sort,
                ...reduceStore(state, storeActions.setSort(makeSort(action.sort)))
            }

        case SELECT:
            return {
                ...state,
                selected: action.flowIds
            }

        default:
            return state
    }
}


const sortKeyFuns = {

    TLSColumn: flow => flow.request.scheme,

    PathColumn: flow => RequestUtils.pretty_url(flow.request),

    MethodColumn: flow => flow.request.method,

    StatusColumn: flow => flow.response && flow.response.status_code,

    TimeColumn: flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start,

    SizeColumn: flow => {
        let total = flow.request.contentLength
        if (flow.response) {
            total += flow.response.contentLength || 0
        }
        return total
    },
}

export function makeFilter(filter) {
    if (!filter) {
        return
    }
    return Filt.parse(filter)
}

export function makeSort({ column, desc }) {
    const sortKeyFun = sortKeyFuns[column]
    if (!sortKeyFun) {
        return
    }
    return (a, b) => {
        const ka = sortKeyFun(a)
        const kb = sortKeyFun(b)
        if (ka > kb) {
            return desc ? -1 : 1
        }
        if (ka < kb) {
            return desc ? 1 : -1
        }
        return 0
    }
}

export function setFilter(filter) {
    return { type: SET_FILTER, filter }
}

export function setHighlight(highlight) {
    return { type: SET_HIGHLIGHT, highlight }
}

export function setSort(column, desc) {
    return { type: SET_SORT, sort: { column, desc } }
}

export function selectRelative(shift) {
    return (dispatch, getState) => {
        let currentSelectionIndex = getState().flows.viewIndex[getState().flows.selected[0]]
        let minIndex              = 0
        let maxIndex              = getState().flows.view.length - 1
        let newIndex
        if (currentSelectionIndex === undefined) {
            newIndex = (shift < 0) ? minIndex : maxIndex
        } else {
            newIndex = currentSelectionIndex + shift
            newIndex = window.Math.max(newIndex, minIndex)
            newIndex = window.Math.min(newIndex, maxIndex)
        }
        let flow = getState().flows.view[newIndex]
        dispatch(select(flow ? flow.id : undefined))
    }
}


export function resume(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}/resume`, { method: 'POST' })
}

export function resumeAll() {
    return dispatch => fetchApi('/flows/resume', { method: 'POST' })
}

export function kill(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}/kill`, { method: 'POST' })
}

export function killAll() {
    return dispatch => fetchApi('/flows/kill', { method: 'POST' })
}


export function remove(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}`, { method: 'DELETE' })
}

export function duplicate(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}/duplicate`, { method: 'POST' })
}

export function replay(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}/replay`, { method: 'POST' })
}

export function revert(flow) {
    return dispatch => fetchApi(`/flows/${flow.id}/revert`, { method: 'POST' })
}

export function update(flow, data) {
    return dispatch => fetchApi.put(`/flows/${flow.id}`, data)
}

export function uploadContent(flow, file, type) {
    const body = new FormData()
    file       = new window.Blob([file], { type: 'plain/text' })
    body.append('file', file)
    return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, { method: 'post', body })
}


export function clear() {
    return dispatch => fetchApi('/clear', { method: 'POST' })
}

export function download() {
    window.location = '/flows/dump'
    return { type: REQUEST_ACTION }
}

export function upload(file) {
    const body = new FormData()
    body.append('file', file)
    return dispatch => fetchApi('/flows/dump', { method: 'post', body })
}


export function select(id) {
    return {
        type: SELECT,
        flowIds: id ? [id] : []
    }
}
