function createStore(reducer) {
    let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
    let cbs = []                     //массив подписчиков

    const getState = () => state            //функция, возвращающая переменную из замыкания
    const subscribe = (cb) => (
        cbs.push(cb),
        () => (cbs = cbs.filter((c) => c !== cb))
    )

    const dispatch = (action) => {
        if (typeof action === 'function') { //если action - не объект, а функция
            return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
        }
        const newState = reducer(state, action) //пробуем запустить редьюсер
        if (newState !== state) { //проверяем, смог ли редьюсер обработать action
            state = newState //если смог, то обновляем state 
            for (let cb of cbs) cb() //и запускаем подписчиков
        }
    }

    return {
        getState, //добавление функции getState в результирующий объект
        dispatch,
        subscribe //добавление subscribe в объект
    }
}

function jwtDecode(token) {
    try {
        return JSON.parse(atob(token.split('.')[1]))
    }
    catch (e) {

    }
}

function authReducer(state = {}, { type, token }) {
    if (type === "AUTH_LOGIN") {
        const payload = jwtDecode(token)
        if (payload) {
            return {
                token, payload
            }
        }
    }
    if (type === "AUTH_LOGOUT") {
        return {}
    }
    return state
}

const actionAuthLogin = (token) =>
    (dispatch, getState) => {
        const oldState = getState()
        dispatch({ type: "AUTH_LOGIN", token })
        const newState = getState()
        if (oldState !== newState) {
            localStorage.setItem('authToken', token);
        }
    }

const actionAuthLogout = () =>
    dispatch => {
        dispatch({ type: "AUTH_LOGOUT" })
        localStorage.removeItem('authToken')
    }

function promiseReducer(state = {}, { type, name, status, payload, error }) {
    ////?????
    //ОДИН ПРОМИС:
    //состояние: PENDING/FULFILLED/REJECTED
    //результат
    //ошибка:
    //{status, payload, error}
    //{
    //       name1:{status, payload, error}   
    //       name2:{status, payload, error}   
    //       name3:{status, payload, error}   
    //}
    if (type === 'PROMISE') {
        //if (name == 'login' && status == 'FULFILLED') {
        //    store.dispatch(actionAuthLogin(payload.data.login))
        //    state = store.getState();
        //}
        return {
            ...state,
            [name]: { status, payload, error }
        }
    }

    return state;
}

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

const actionPromise = (name, promise) =>
    async (dispatch) => {
        try {
            dispatch(actionPending(name))
            let payload = await promise
            dispatch(actionFulfilled(name, payload))
            if (name === 'login') {
                store.dispatch(actionAuthLogin(payload.data.login))
            }
            return payload
        }
        catch (e) {
            console.log(e);
            dispatch(actionRejected(name, e))
        }
    }

function cartReducer(state = {}, { type, count = 1, good }) {
    // type CART_ADD CART_REMOVE CAT_CLEAR CART_DEC
    // {
    //  id1: {count: 1, good{name, price, images, id}, total: price}
    // }
    if (type === "CART_ADD") {
        return {
            ...state,
            [good._id]: { count: count + (state[good._id]?.count || 0), good }

        }
    }
    if (type === "CART_CLEAR") {
        return {}
    }
    if (type === "CART_REMOVE") {
        let newState = { ...state }
        delete newState[good._id]
        return newState
    }
    if (type === "CART_DELETE") {
        if (state[good._id].count > 1) {
            return {
                ...state, [good._id]: { count: -count + (state[good._id]?.count || 0), good },
            };
        }
    }

    return state
}

const cartAdd = (good, count = 1) => ({ type: 'CART_ADD', good, count })
const cartClear = () => ({ type: 'CART_CLEAR' })
const cartRemove = (good) => ({ type: 'CART_REMOVE', good })
const cartDelete = (good) => ({ type: 'CART_DELETE', good })

/* store.dispatch({type: "CART_ADD", good: {_id: "чипсы"}})  ПРОБНЫЙ СТОР!!! */

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

function combineReducers(reducers) {
    function combinedReducer(combinedState = {}, action) {
        const newCombinedState = {}
        for (const [reducerName, reducer] of Object.entries(reducers)) {
            const newSubState = reducer(combinedState[reducerName], action)
            if (newSubState !== combinedState[reducerName]) {
                newCombinedState[reducerName] = newSubState
            }
        }
        if (Object.keys(newCombinedState).length === 0) {
            return combinedState
        }
        return { ...combinedState, ...newCombinedState }
    }

    return combinedReducer
}

const store = createStore(
    combineReducers({ auth: authReducer, promise: promiseReducer, cart: cartReducer })
) //не забудьте combineReducers если он у вас уже есть


const enterLogin = document.getElementById('enterLogin')
const outLogin = document.getElementById('outLogin')
const enterLogout = document.getElementById('enterLogout')
const loginContainer = document.getElementById('mainLoginContainer')
const enterCart = document.getElementById('enterCart')
const cartContainer = document.getElementById('cartContainer')
const leaveCart = document.getElementById('closeCart')
const cartProductsContainer = document.getElementById('cartProductsContainer')
const enterRegister = document.getElementById('enterRegister')
const registerContainer = document.getElementById('registerContainer')
const hideLogin = document.getElementById('hideLogin')
const outRegister = document.getElementById('outRegister')


if (localStorage.authToken) {
    store.dispatch(actionAuthLogin(localStorage.authToken))
    enterLogin.style.display = 'none';
    enterCart.style.display = 'block'
    enterLogout.style.display = 'block';
}
//const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
store.subscribe(() => console.log(store.getState()))



const gql = (url, query, variables) => fetch(url, {
    method: 'POST',
    headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
    },
    body: JSON.stringify({ query, variables })
}).then(res => res.json())

const backendURL = 'http://shop-roles.node.ed.asmer.org.ua'
const graphQLBackendUrl = `${backendURL}/graphql` 


const actionRootCats = () =>
    actionPromise('rootCats', gql(graphQLBackendUrl, `query {
        CategoryFind(query: "[{\\"parent\\":null}]"){
            _id name
        }
    }`))

const actionCatById = (_id) =>  //добавить подкатегории
    actionPromise('catById', gql(graphQLBackendUrl, `query  categoryById($q: String){
        CategoryFindOne(query: $q){
            _id name goods {
                _id name price images {
                    url
                }
            }
        }
    }`, { q: JSON.stringify([{ _id }]) }))


const actionGoodById = (_id) =>
    actionPromise('goodById', gql(graphQLBackendUrl, `query goodById($q: String){
        GoodFindOne(query: $q){
            _id name description price
        }
    }`, { q: JSON.stringify([{ _id }]) }))


const actionLogin = (login, password) =>
    actionPromise('login', gql(graphQLBackendUrl,
        `query log($login: String, $password: String){
            login(login:$login, password: $password)
        }`
        , { login, password }))


 /*        actionPromise(
            "register",
            gql(
                `mutation register($login: String, $password: String) {
                UserUpsert(user: {login: $login, password: $password}) {
                   _id
                   login
                 }
               }`,
                { login: login, password: password }
            )
        ) */


store.dispatch(actionRootCats())

/* const actionEnterLogin = () */

enterLogin.onclick = () => {
    return loginContainer.style.display = 'block'
}

outLogin.onclick = () => {
    return loginContainer.style.display = 'none'
}

enterLogout.onclick = () => {
    store.dispatch(actionAuthLogout())
}

enterRegister.onclick = () => {
    return hideLogin.style.display = 'none', registerContainer.style.display = 'block'
}

outRegister.onclick = () => {
    return hideLogin.style.display = 'block', registerContainer.style.display = 'none'
}

const updateCartInfo = () => {
    cartProductsContainer.innerHTML = '';
    cartContainer.style.display = 'block';
    const cart = store.getState().cart;
    const ul = document.createElement('ul')
    cartProductsContainer.append(ul)

    const clearFromCartButton = document.createElement('button')
        clearFromCartButton.innerHTML = "Очистить"
        clearFromCartButton.onclick = () => { store.dispatch(cartClear())}
        cartProductsContainer.append(clearFromCartButton)

    const buyGoods = document.createElement('button')
        buyGoods.innerHTML = "Купить"
        buyGoods.onclick = () => {store.dispatch(cartClear()), alert('Спасибо за покупку!')}
        cartProductsContainer.append(buyGoods)

    for (const [key, value] of Object.entries(cart)) {
        const li = document.createElement('li')
        const a = document.createElement('a')
        const removeFromCartButton = document.createElement('button')
        removeFromCartButton.innerText = "-"
        removeFromCartButton.onclick = () => { store.dispatch(cartRemove(value.good)) }
       
        a.innerHTML = value.good.name;
        ul.append(li)
        li.append(a)
        if (value.good.images) {
            for (let i = 0; i < value.good.images.length; i++) {
                const imgElement = document.createElement('img')
                imgElement.src = `${backendURL}/${value.good.images[i].url}`;
                imgElement.style.height = '100px';
                imgElement.style.width = '200px';
                li.append(imgElement)
            }
        }

        ul.append(removeFromCartButton)
        if (value.count > 1) {

            const deleteFromCartButton = document.createElement('button')
            deleteFromCartButton.innerHTML = "-1"
            deleteFromCartButton.onclick = () => { store.dispatch(cartDelete(value.good))}
            ul.append(deleteFromCartButton)
        } 
        const allCountElement = document.createElement('p');
        allCountElement.innerHTML = `Total: ${value.count}`;
        ul.append(allCountElement)
    }
    
};

enterCart.onclick = () => {
    updateCartInfo();
}

leaveCart.onclick = () => {
    cartProductsContainer.innerHTML = '';
    cartContainer.style.display = 'none';
}


const loginBtn = document.getElementById('loginBtn')
loginBtn.onclick = handleLogin

function handleLogin() {
    let userName = document.getElementById('inputUserName')
    let userPassword = document.getElementById('inputPassword')
    store.dispatch(actionLogin(userName.value, userPassword.value));
    outLogin.click();
}


/* store.dispatch(actionLogin('levshin95', '123123')) */


store.subscribe(() => {
    if (cartContainer.style.display === 'block' && store.getState().cart) {
        updateCartInfo();
    }
})

store.subscribe(() => {
    const rootCats = store.getState().promise.rootCats?.payload?.data.CategoryFind
    if (!rootCats) {
        aside.innerHTML = '<img src="Loading_icon.gif">'
    } else {
        aside.innerHTML = ''
        const ul = document.createElement('ul')
        aside.append(ul)
        for (let { _id, name } of rootCats) {
            const li = document.createElement('li')
            const a = document.createElement('a')
            a.href = "#/category/" + _id
            a.innerHTML = name
            ul.append(li)
            li.append(a)
        }
    }
})

const displayCategory = function () {
    const catById = store.getState().promise.catById?.payload?.data.CategoryFindOne
    const [, route] = location.hash.split('/')
    if (catById && route === 'category') {
        const { name, goods, _id } = catById
        categories.innerHTML = `<h1>${name}</h1>`
        const ul = document.createElement('ul')
        categories.append(ul)
        for (let good of goods) {
            const li = document.createElement('li')
            const a = document.createElement('a')
            a.href = "#/good/" + good._id
            a.innerHTML = good.name
            ul.append(li)
            li.append(a)
            if (good.images) {
                for (let i = 0; i < good.images.length; i++) {
                    const imgElement = document.createElement('img')
                    imgElement.src = `${backendURL}/${good.images[i].url}`;
                    imgElement.style.height = '100px';
                    imgElement.style.width = '200px';
                    li.append(imgElement)
                }
            }

            const authToken = store.getState().auth?.token;

            if (authToken) {
                const addToCartButton = document.createElement('button')
                addToCartButton.innerText = "+"
                addToCartButton.onclick = () => { store.dispatch(cartAdd(good, 1)) }
                ul.append(addToCartButton)
            }


        }
    }
}

store.subscribe(() => {
    const authToken = store.getState().auth?.token;
    if (!authToken) {
        enterLogout.style.display = 'none';
        enterCart.style.display = 'none';
        enterLogin.style.display = 'block';
        displayCategory();
    } else {
        debugger
        enterLogin.style.display = 'none';
        enterCart.style.display = 'block';
        enterLogout.style.display = 'block';
        displayCategory();
        /* alert(`Hello, ${store.getState().auth?.payload?.sub?.login}`); */
    }
})



store.subscribe(() => {
    displayCategory();
})

store.subscribe(() => {

    const goodById = store.getState().promise.goodById?.payload?.data.GoodFindOne
    const [, route] = location.hash.split('/')
    if (goodById && route === 'good') {
        const { name, description, _id, price, images} = goodById
        categories.innerHTML = `<h1>${name}</h1>`

        const strongPrice = document.createElement('b')
        const div = document.createElement('div')

        categories.appendChild(div)
        categories.appendChild(strongPrice)

        categories.appendChild(strongPrice)

        div.innerText = description
        strongPrice.innerHTML = price
    }
})

store.subscribe(() => { })



/* store.subscribe(() => {
    const goodById = store.getState().promise.goodById?.payload?.data.CategoryFindOne
    const [,route] = location.hash.split('/')
    if (catById && route === 'good') {
        const {name, goods, _id} = catById
        categories.innerHTML = `<h1>${name}</h1>`
        // нарисовать картинки, описание, цену и т.д.
    }
}) */

window.onhashchange = () => {
    const [, route, _id] = location.hash.split('/')
    console.log(route, _id)
    const routes = {
        category() {
            store.dispatch(actionCatById(_id))
        },
        good() {
            store.dispatch(actionGoodById(_id))
        }
    }
    if (route in routes) {
        routes[route]()
    }
}

window.onhashchange()
