
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)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка

    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 }) {
    //{
    //  token, payload
    //}

    if (type === 'AUTH_LOGIN') {
        //пытаемся токен раскодировать
        const payload = jwtDecode(token)
        if (payload) { //и если получилось
            return {
                token, payload //payload - раскодированный токен;
            }
        }
    }
    if (type === 'AUTH_LOGOUT') {
        return {}
    }
    return state;
}

function countReducer(state = { count: 0 }, { type }) {
    if (type === "COUNT_INC") {
        return {
            count: state.count + 1
        }
    }
    if (type === "COUNT_DEC") {
        return {
            count: state.count - 1
        }
    }
    return state
}

function localStoreReducer(reducer, localStorageKey) {
    function localStoredReducer(state, action) {
        // Если state === undefined, то достать старый state из local storage
        if (state === undefined) {
            try {
                return JSON.parse(localStorage[localStorageKey])
            } catch (e) { }

        }
        const newState = reducer(state, action)
        // Сохранить newState в local storage
        localStorage[localStorageKey] = JSON.stringify(newState)
        return newState

    }
    return localStoredReducer
}

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') {
        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))
            return payload
        }
        catch (e) {
            dispatch(actionRejected(name, e))
        }
    }

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


function combineReducers(reducers) { //пачку редьюсеров как объект {auth: authReducer, promise: promiseReducer}
    function combinedReducer(combinedState = {}, action) { //combinedState - типа {auth: {...}, promise: {....}}
        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 //нам возвращают один редьюсер, который имеет стейт вида {auth: {...стейт authReducer-а}, promise: {...стейт promiseReducer-а}}
}

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

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

        if (state[good._id].count === 1) {
            let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
            //delete newState[good._id]
            return newState;
        }
    }

    if (type === "CART_CLEAR") {
        return {};
    }
    if (type === "CART_REMOVE") {
        // let newState = {...state}
        let { [good._id]: id1, ...newState } = state; //o4en strashnoe koldunstvo
        //delete newState[good._id]
        return newState;
    }

    return state;
}




const backendURL = 'http://shop-roles.node.ed.asmer.org.ua/'
//store.dispatch(actionPromise('delay1000', delay(1000))) 
//store.dispatch(actionPromise('delay3000', delay(3000))) 

//store.dispatch(actionPending('delay1000'))

//delay(1000).then(result => store.dispatch(actionFulfilled('delay1000', result)),
//error => store.dispatch(actionRejected('delay1000', error)))

//store.dispatch(actionPending('delay3000'))

//delay(3000).then(result => store.dispatch(actionFulfilled('delay3000', result)),
//error => store.dispatch(actionRejected('delay3000', error)))

const getGQL = (url) => (query, variables) =>
    fetch(url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            ...(localStorage.authToken
                ? { Authorization: "Bearer " + localStorage.authToken }
                : {}),
        },
        body: JSON.stringify({ query, variables }),
    })
        .then((res) => res.json())
        .then((data) => {
            if (data.data) {
                return Object.values(data.data)[0];
            } else throw new Error(JSON.stringify(data.errors));
        });

const gql = getGQL(backendURL + "graphql");

// 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/graphql'


const actionRootCats = () =>
    actionPromise(
        'rootCats',
        gql(

            `query {
                CategoryFind(query: "[{\\"parent\\":null}]"){
                _id name
        }
    }`
        )
    )

const actionCatById = (_id) =>  //добавить подкатегории
    actionPromise(
        'catById',
        gql(

            `query catById($q: String){
                CategoryFindOne(query: $q){
                    _id name goods {
                        _id name price images {
                            url
                }
            }
        }
    }`,
            { q: JSON.stringify([{ _id }]) }
        )
    )

const actionGoodById = (_id) =>
    actionPromise(
        'goodByID',
        gql(

            `query goodByID($q:String){
                GoodFindOne(query: $q){
                    _id
                    name
  	                description
                    price
                    categories{
                        _id
                        name
                    }
                    images{
                        url
                    }
                }
            }`,
            { q: JSON.stringify([{ _id }]) }
        )
    )

const actionRegistr = (login, password) =>
    actionPromise(
        'registr',
        gql(

            `mutation register($login:String, $password:String){
                UserUpsert(user: {login:$login, password:$password}){
                  _id login 
                }
              }`,
            { login: login, password: password }
        )
    )

const actionLogin = (login, password) =>
    actionPromise(
        'login',
        gql(

            `query log($login:String, $password:String){
                login(login:$login, password:$password)
            }`,
            { login: login, password: password }
        )
    )

    const actionOrder = () => async (dispatch, getState) => {
        let { cart } = getState();
        const orderGoods = Object.entries(cart).map(([_id, { count }]) => ({
            good: { _id },
            count,
        }));
    
        let result = await dispatch(
            actionPromise(
                "order",
                gql(
                    `
                        mutation newOrder($order:OrderInput){
                        OrderUpsert(order:$order)
                            { _id total  }
                        }
                `,
                    { order: { orderGoods } }
                )
            )
        );
        if (result?._id) {
            dispatch(actionCartClear());
            document.location.hash = "#/cart/";
            alert("Покупка успішна")
        }
    
    };

    const orderHistory = () =>
    actionPromise(
        "history",
        gql(` query OrderFind{
        OrderFind(query:"[{}]"){
            _id total createdAt orderGoods{
                count good{
                    _id name price images{
                        url
                    }
                }
                owner{
                    _id login 
                }
            }
        }
    }
    `)
    );

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

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

const actionCartAdd = (good, count = 1) => ({
    type: "CART_ADD",
    good,
    count
});
const actionCartChange = (good, count = 1) => ({
    type: "CART_CHANGE",
    good,
    count,
}); ///oninput меняяем полностью
const actionCartDelete = (good) => ({
    type: "CART_DELETE",
    good
});
const actionCartClear = () => ({
    type: "CART_CLEAR"
});

const actionFullLogin = (login, password) => async (dispatch) => {
    let token = await dispatch(actionLogin(login, password))
    if (token) {
        dispatch(actionAuthLogin(token))
    }
}

const actionFullRegistr = (login, password) => async (dispatch) => {
    try {
        await dispatch(actionRegistr(login, password))
    }
    catch (e) {
        return console.log(e)
    }
    await dispatch(actionFullLogin(login, password))
}

const store = createStore(combineReducers({ auth: authReducer, promise: promiseReducer, cart: localStoreReducer(cartReducer, "cart") })) //не забудьте combineReducers если он у вас уже есть
if (localStorage.authToken) {
    store.dispatch(actionAuthLogin(localStorage.authToken))
}
//const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer, cart: cartReducer}))
store.subscribe(() => console.log(store.getState()))

store.dispatch(actionRootCats())
// store.dispatch(actionLogin('test456', '123123'))



store.subscribe(() => {
    const { rootCats } = store.getState().promise
    if (rootCats?.payload) {
        aside.innerHTML = ''
        for (let { _id, name } of rootCats?.payload) {
            const a = document.createElement('a')
            a.href = `#/category/${_id}`
            a.innerHTML = name
            aside.append(a)
        }
    }
})


store.subscribe(() => {
    const { catById } = store.getState().promise
    const [, route, _id] = location.hash.split('/')
    if (catById?.payload && route === 'category') {
        const { name, goods, _id } = catById?.payload
        main.innerHTML = `<h1>${name}</h1>`
        for (let { _id, name, price, images } of goods) {
            const cards = document.createElement("div")
            cards.innerHTML = `<h2>${name}</h2>
                                <img src="${backendURL}/${images[0].url}"/>    
                                 <strong>Ціна ${price} грн</strong>                          
                                 <a href="#/good/${_id}">Перейти</a> `;
            main.append(cards);



            // const a = document.createElement('a')
            // const p = document.createElement('p')
            // p.href = `#/good/${_id}`
            // a.href  = `#/good/${_id}`
            // a.innerHTML = name
            // p.innerHTML = price
            // main.append(a)
            // main.append(p)
        }
    }
})


store.subscribe(() => {
    const { goodByID } = store.getState().promise
    const [, route, _id] = location.hash.split('/')
    if (goodByID?.payload && route === 'good') {
        main.innerHTML = ""
        const { name, images, price, description } = goodByID?.payload
        // main.innerHTML = `<h1>${name}</h1> Продукт`
        const cards = document.createElement("div")
        cards.innerHTML = `<h2>${name}</h2>
                            <img src="${backendURL}/${images[0].url}"/> 
                            <strong> Ціна ${price} грн</strong>
                            <button id='buy'> Придбати</button>
                            <p>${description}</p>`;
        main.append(cards)
        cards.style.marginTop = "10px"
        var btn = document.getElementById('buy')
        btn.onclick = () => {
            store.dispatch(actionCartAdd(goodByID.payload))

        }
    }
})


const bPopupContent = document.createElement("div");
const obertkaDlyaTovara = document.createElement("div")

const all = document.createElement('h2')
const checkout = document.createElement("button")
const clearToCart = document.createElement("button")
store.subscribe(() => {
    obertkaDlyaTovara.innerHTML = ""
    const cartById = store.getState().cart
    let productCount = 0;
    let productPrice = 0
    for (let gPC of Object.values(cartById)) {
        const { good,count } = gPC
        productCount += count
        productPrice += good.price * count
        const tovar = document.createElement("div")
        tovar.id = "tovar"
        tovar.style.border = "3px solid blue"
        tovar.style.marginTop = "10px"
        const name = document.createElement('h1')
        const price = document.createElement('h3')
        const countById = document.createElement('p')
        const divDlyaKnopok = document.createElement("div")
        const plus = document.createElement("button")
        const minus = document.createElement("button")
        plus.innerText = "+"
        minus.innerText = "-"
        tovar.append(name)
        tovar.append(price)
        tovar.append(countById)
        divDlyaKnopok.append(plus)
        divDlyaKnopok.append(minus)
        tovar.append(divDlyaKnopok)
        name.innerHTML = good.name
        price.innerHTML = good.price
        countById.innerHTML = count
        obertkaDlyaTovara.append(tovar)
        bPopupContent.append(obertkaDlyaTovara)
        
        plus.onclick = () => {
            store.dispatch(actionCartAdd(good))
        }
        minus.onclick = () => {
            store.dispatch(actionCartDelete(good))
        }
        
    }
        clearToCart.id = "clearToCart"
        clearToCart.innerHTML = "Очистити кошик"
        clearToCart.style.margin = "0 auto"
        clearToCart.style.marginBottom = "20px"
        clearToCart.style.background = "blue"
        clearToCart.style.color = "yellow"
        bPopupContent.append(clearToCart)
        checkout.id = "checkout"
        checkout.innerHTML = "Оформити замовлення"
        checkout.style.margin = "0 auto"
        checkout.style.background = "blue"
        checkout.style.color = "yellow"
        bPopupContent.append(checkout)
        all.id = "all"
        all.innerHTML = "Всього: " + productPrice
        bPopupContent.append(all)
        all.style.marginLeft = "90%"
        clearToCart.onclick = () => {
            all.innerHTML = " "
            store.dispatch(actionCartClear())
            
        }

        checkout.onclick = () => {
            all.innerHTML = " "
            store.dispatch(actionOrder());
            store.dispatch(orderHistory());
            
        }
    })



store.subscribe(() => {
    batton.onclick = () => {
        store.dispatch(actionFullLogin(login.value, password.value))

    }
    battonchik.onclick = () => {
        store.dispatch(actionFullRegistr(login.value, password.value))

    }

    battonSMakom.onclick = () => {
        store.dispatch(actionAuthLogout())

    }
    const payload = store.getState().auth.token;
    if (payload) {
        korzina.style.display = "block"
        login.style.display = "none"
        password.style.display = "none"
        batton.style.display = "none"
        battonchik.style.display = "none"
        battonSMakom.style.display = "block"
        accaunt.style.display = "block"
        accaunt.innerText = jwtDecode(payload).sub.login;
        purchaseHistory.style.display = "block"

    } else {
        korzina.style.display = "none"
        battonSMakom.style.display = "none"
        login.style.display = "block"
        password.style.display = "block"
        batton.style.display = "block"
        battonchik.style.display = "block"
        accaunt.style.display = "none"
        purchaseHistory.style.display = "none"
    }

})
store.dispatch(orderHistory());
const h2 = document.createElement("h2")
store.subscribe(() => {
    const { history } = store.getState().promise;
    const [, route] = location.hash.split("/");
    purchaseHistory.onclick = () => {
        const bPopup = document.createElement("div");
        const bPopupContent = document.createElement("div");
        bPopup.id = "b-popup";
        bPopup.className = "b-popup";
        bPopupContent.className = "b-popup-content b-poput-container-flex";
        header.append(bPopup);
        bPopup.append(bPopupContent);
        const buttonCloseCart = document.createElement("button");
        buttonCloseCart.innerText = "×";
        buttonCloseCart.id = "buttonCloseCartId";
        bPopupContent.append(buttonCloseCart);
        buttonCloseCart.onclick = () => {
            var parent = document.getElementById("header");
            var child = document.getElementById("b-popup");
            parent.removeChild(child);
        };
       

        for (let [key, value] of Object.entries(history.payload)) {
            const { _id, createdAt, total, orderGoods } = value;
            const h2 = document.createElement("h2");
            h2.className = "h2History"
            const dateOfOrder = new Date(+createdAt);
            h2.innerHTML = `${dateOfOrder.toLocaleDateString()} ${dateOfOrder.toLocaleTimeString()}
                            Order ID: ${_id} от , c ${orderGoods.length} goods worth: ${total}`;
                            bPopupContent.append(h2);
        
            
        }

        if (Object.keys(history.payload).length == 0) {
            const p = document.createElement("p");
            p.innerHTML = "<p>Ще немає покупок</p>";
            card.append(p);
        }

       
    };
    
});


const buttonCloseCart = document.createElement("button");
buttonCloseCart.innerText = `×`;
buttonCloseCart.id = "buttonCloseCartId";
bPopupContent.append(buttonCloseCart);

buttonCloseCart.onclick = () => {
    var parent = document.getElementById("header");
    var child = document.getElementById("b-popup");
    parent.removeChild(child);
};
function bPopupCreate() {
    const bPopup = document.createElement("div");
    bPopup.id = "b-popup";
    bPopup.className = "b-popup";
    bPopupContent.className = "b-popup-content b-poput-container-flex";
    header.append(bPopup);
    bPopup.append(bPopupContent);
}
korzina.onclick = () => {
    bPopupCreate()
}



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

    if (route in routes) {
        routes[route]()
    }

}

window.onhashchange()




