function createStore(reducer){
    let state = reducer(undefined, {})
    let cbs   = []
    function dispatch(action){
        if (typeof action === 'function'){
            return action(dispatch)
        }
        const newState = reducer(state, action)
        if (state !== newState){
            state = newState
            cbs.forEach(cb => cb())
        }
    }
    return {
        dispatch,
        subscribe(cb){
            cbs.push(cb)
            return () =>  cbs = cbs.filter(c => c !== cb)
        },
        getState(){
            return state
        }
    }
}

function promiseReducer(state={}, {type, status, payload, error, name}){
    if (type === 'PROMISE'){
        return {
            ...state,
            [name]:{status, payload, error}
        }
    }
    return state
}


const store = createStore(promiseReducer)
const unsubscribe1 = store.subscribe(() => console.log(store.getState()))

const actionPending = name => ({type: 'PROMISE', status: 'PENDING', name})
const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})

const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))

const actionPromise = (name, promise) => 
    async dispatch => {
        dispatch(actionPending(name))
        try{
            let payload = await promise
            dispatch(actionResolved(name, payload)) 
            return payload
        }
        catch(error){
             dispatch(actionRejected(name, error))
        }
    }

    const getGQL = url => {
        return function(query, variables={}) {
            return fetch(url, 
            {
                method: "POST",
                headers: 
                {"Content-Type": "application/json",
                ...(localStorage.authToken ? {Authorization: localStorage.authToken} : {})
                },
                body: JSON.stringify({query, variables})
            }).then(resp => resp.json())
            .then(data => {
                if ("errors" in data) {
                    throw new Error('ашипка, угадывай што не так')
                }
                else {
                    return data.data[Object.keys(variables)[0]]
                }
            })
        }
    }

let shopGQL = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql')

const goodById = goodId => {
    let id = `[{"_id":"${goodId}"}]`
    return shopGQL(`
    query good($id:String){
        GoodFindOne(query: $id) {
          name description price images {
            _id text url
          }
          categories {
            _id name
          }
        }
    }`, {GoodFindOne: '', id })
}

const actionGoodById = id => 
    actionPromise('goodById', goodById(id))

const actionRootCategories = () =>
    actionPromise('rootCategories', shopGQL(`
            query cats($query:String){
              CategoryFind(query:$query){
                _id name 
              }
            }
        `, {CategoryFind:'', query: JSON.stringify([{parent:null}])}))

const actionCategoryById = (_id) => 
    actionPromise('catById', shopGQL(`query catById($query:String){
                                          CategoryFindOne(query:$query){
                                            _id name goods{
                                              _id name price description images{
                                                url
                                              }
                                            }
                                          }
                                        }`, {CategoryFindOne:'', query: JSON.stringify([{_id}])}))


store.dispatch(actionRootCategories())

window.onhashchange = () => {
    let {1: route, 2:id} = location.hash.split('/')
    if (route === 'categories'){
        store.dispatch(actionCategoryById(id))
    }

    if (route === 'good'){
        store.dispatch(actionGoodById(id))
    }
}

function drawMainMenu(){
    let cats = store.getState().rootCategories.payload
    if (cats){ //каждый раз дорисовываются в body
        aside.innerText = ''
        for (let {_id, name} of cats){
            let catA = document.createElement('a')
            catA.href = `#/categories/${_id}`
            catA.innerText = name
            aside.append(catA)
        }
    }
}

store.subscribe(drawMainMenu)



store.subscribe(() => {
    const {1: route, 2:id} = location.hash.split('/')
    if (route === 'categories'){
        const catById = store.getState().catById?.payload
        if (catById){
            main.innerText = ''
            let categoryName = document.createElement('div')
            categoryName.innerText = catById.name
            categoryName.style.fontSize = '25px'
            categoryName.style.fontWeight = 'bold'
            main.append(categoryName)
            for (let {_id, name} of catById.goods){
                let good = document.createElement('a')
                    good.href = `#/good/${_id}`
                    good.innerText = name

                let btn = document.createElement('button')
                    btn.style.cursor = 'pointer'
                    btn.innerText = 'купыть'
                main.append(good, btn)
            }
        }
    }
    if (route === 'good'){
        const goodById = store.getState().goodById?.payload
        if (goodById){
            main.innerText = ''
            let {name, description, price} = goodById
            let goodName = document.createElement('div')
                goodName.innerText = name
                goodName.style.fontSize = '35px'
                goodName.style.fontWeight = 'bold'
                goodName.style.marginBottom = '25px'

            let goodDescription = document.createElement('div')
                goodDescription.innerText = description
                goodDescription.style.marginBottom = '25px'

            let goodPrice = document.createElement('div')
                goodPrice.innerText = 'Цена: ' + price
                goodPrice.style.marginBottom = '5px'

            let btn = document.createElement('button')
                btn.style.cursor = 'pointer'
                btn.innerText = 'купыть'

            main.append(goodName, goodDescription, goodPrice, btn)
        }
    }
})
