let loginValue = "";
let passwordValue = "";

const authDiv = document.createElement("div");
const noAuthDiv = document.createElement("div");
const cartIcon = document.createElement("div");
const loginButton = document.createElement("button");
const registerButton = document.createElement("button");
const nameDiv = document.createElement("div");
const logoutButton = document.createElement("button");

cartIcon.innerHTML = "<a href = '#/cart'>Cart</a>";
loginButton.innerText = "Login";
loginButton.onclick = () => {
    window.location = "#/login";
};
registerButton.innerText = "Register";
registerButton.onclick = () => {
    window.location = "#/register";
};
logoutButton.innerText = "Logout";

noAuthDiv.append(loginButton);
noAuthDiv.append(registerButton);

authDiv.append(nameDiv);
authDiv.append(logoutButton);
authDiv.append(cartIcon);

cartIcon.style.marginLeft = "auto";
authDiv.style.display = "flex";

header.append(authDiv);
header.append(noAuthDiv);

const jwtDecode = (token) => {
    try {
        let payload = JSON.parse(atob(token.split(".")[1]));

        return payload;
    } catch (e) {
        console.log(e);
    }
};

function createStore(reducer) {
    let state = reducer(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") {
            return action(dispatch, getState);
        }
        const newState = reducer(state, action);
        if (newState !== state) {
            state = newState;
            for (let cb of cbs) cb();
        }
    };

    return {
        getState,
        dispatch,
        subscribe,
    };
}

function cartReducer(state = {}, { type, good, count = 1 }) {
    if (!Object.keys(state).length) {
        let savedData =
            (localStorage[jwtDecode(localStorage.authToken)?.sub.login] &&
                JSON.parse(localStorage[jwtDecode(localStorage.authToken)?.sub.login])) ||
            null;
        if (savedData) {
            state = savedData?.cart || {};
        } else {
            state = {};
        }
    }
    if (count <= 0) {
        type = "CART_DELETE";
    }

    if (type === "CART_ADD") {
        return {
            ...state,
            [good["_id"]]: {
                good,
                count: good["_id"] in state ? state[good._id].count + count : count,
            },
        };
    }
    if (type === "CART_CHANGE") {
        return {
            ...state,
            [good["_id"]]: {
                good,
                count: count,
            },
        };
    }
    if (type === "CART_DELETE") {
        let { [good._id]: toRemove, ...newState } = state;
        return newState;
    }
    if (type === "CART_CLEAR") {
        return {};
    }
    return state;
}

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

function authReducer(state, { type, token }) {
    if (state === undefined) {
        if (localStorage.authToken) {
            token = localStorage.authToken;
            type = "AUTH_LOGIN";
            state = {};
        }
    }

    if (type === "AUTH_LOGIN") {
        if (!token || !jwtDecode(token)) return {};
        localStorage.authToken = token;
        return {
            ...state,
            token: token,
            payload: jwtDecode(token),
        };
    }

    if (type === "AUTH_LOGOUT") {
        localStorage.removeItem("authToken");
        return {};
    }
    return state || {};
}

const combineReducers =
    (reducers) =>
    (state = {}, action) => {
        let newState = {};

        for (let [key, reducer] of Object.entries(reducers)) {
            let reducerResult = reducer(state[key], action);
            if (state[key] !== reducerResult) {
                newState = { ...newState, [key]: reducerResult };
            }
        }

        if (Object.keys(newState).length) {
            state = { ...state, ...newState };
        }
        return state;
    };

const store = createStore(combineReducers({ auth: authReducer, promise: promiseReducer, cart: cartReducer }));

store.subscribe(() => console.log(store.getState()));
store.subscribe(() => {
    let {
        cart,
        auth: { payload },
    } = store.getState();
    let localStorageLoginSave = {};
    let login = payload?.sub.login || null;
    if (!login) {
        return;
    }

    if (localStorage[login]) {
        localStorageLoginSave = JSON.parse(localStorage[login]);
        if (Object.keys(cart).length === 0) {
            let { cart, ...newState } = localStorageLoginSave;
            localStorage[login] = JSON.stringify(newState);
            return;
        }
    }

    if (Object.keys(cart).length) {
        localStorage[login] = JSON.stringify({ ...localStorageLoginSave, cart });
    }
});

const actionPending = (name) => ({ type: "PROMISE", name, status: "PENDING" });
const actionFulfilled = (name, payload) => ({ type: "PROMISE", name, status: "FULFILLED", payload });
const actionRejected = (name, error) => ({ type: "PROMISE", name, status: "REJECTED", error });
const actionPromise = (name, promise) => async (dispatch) => {
    dispatch(actionPending(name));
    try {
        let payload = await promise;
        dispatch(actionFulfilled(name, payload));
        return payload;
    } catch (error) {
        dispatch(actionRejected(name, 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.errors) {
                throw new Error(JSON.stringify(data.errors));
            } else return Object.values(data.data)[0];
        });

const backendURL = "http://shop-roles.asmer.fs.a-level.com.ua";

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

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

const actionGoodById = (_id) =>
    actionPromise(
        "good",
        gql(
            `query GoodById($q: String){
            GoodFindOne(query: $q){
                _id name  price images{
                    url
                }
            }
        }`,
            { q: JSON.stringify([{ _id }]) }
        )
    );

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 actionOrders = () => (dispatch) =>
    dispatch(
        actionPromise(
            "orders",
            gql(`
            query orders{
                OrderFind(query:"[{}]"){
                    _id total createdAt orderGoods{
                        _id count price good{
                            name _id price images{
                                url
                            }
                        }
                    }
                }
            }
          `)
        )
    );

const actionNewOrder =
    (orderGoods = []) =>
    async (dispatch, getState) => {
        await dispatch(
            actionPromise(
                "newOrder",
                gql(
                    `mutation newOrder($order:OrderInput){
                OrderUpsert(order:$order){
                  _id total
                }
              }
              `,
                    {
                        order: {
                            orderGoods: orderGoods,
                        },
                    }
                )
            )
        );
        let {
            promise: { newOrder },
        } = getState();
        if (newOrder.status === "FULFILLED") {
            dispatch(actionCartClear());
        }
    };

const actionAuthLogin = (token) => ({
    type: "AUTH_LOGIN",
    token: token,
});

const actionLogin = (login, password) => async (dispatch) => {
    const token = await dispatch(
        actionPromise(
            "login",
            gql(
                `query log($login:String,$password:String){
                            login(login:$login,password:$password)
                        }
                        `,
                {
                    login: login,
                    password: password,
                }
            )
        )
    );
    await dispatch(actionAuthLogin(token));
    window.location = "#/home";
};

const actionAuthLogout = () => ({ type: "AUTH_LOGOUT" });

const actionLogout = () => async (dispatch) => {
    await dispatch(actionAuthLogout());
    await dispatch(actionCartClear());
    window.location = "#/home";
};

const actionRegister = (login, password) => async (dispatch) => {
    await dispatch(
        actionPromise(
            "register",
            gql(
                `mutation register($login:String,$password:String){
                               UserUpsert(user:{login:$login,password:$password}){
                                   _id login
                               }
                           }`,
                {
                    login: login,
                    password: password,
                }
            )
        )
    );

    await dispatch(actionLogin(loginValue, passwordValue));
};
const actionCartAdd = (good, count = 1) => ({ type: "CART_ADD", good, count });
const actionCartChange = (good, count = 1) => ({ type: "CART_CHANGE", good, count });
const actionCartDelete = (good) => ({ type: "CART_DELETE", good });
const actionCartClear = () => ({ type: "CART_CLEAR" });

store.dispatch(actionRootCats());

// store.dispatch(actionCartAdd({ _id: "пиво", name: "одеколонь", price: 30 }));

// //{пиво: {count: 1, good: {_id: 'пиво', name: 'одеколонь', price: 30}}}
// store.dispatch(actionCartAdd({ _id: "чипсы", name: "одеколонь", price: 30 }));
// //{пиво: {count: 1, good: {_id: 'пиво', name: 'одеколонь', price: 30}},
// // чипсы: {count: 1, good: {_id: 'чипсы', name: 'огурец с молоком', price: 50}},
// //}
// store.dispatch(actionCartAdd({ _id: "пиво", name: "одеколонь", price: 30 })); //count: 2
// //{пиво: {count: 2, good: {_id: 'пиво', name: 'одеколонь', price: 30}},
// // чипсы: {count: 1, good: {_id: 'чипсы', name: 'огурец с молоком', price: 50}},
// //}
// store.dispatch(actionCartChange({ _id: "чипсы", name: "одеколонь", price: 30 }, 2));
// //{пиво: {count: 2, good: {_id: 'пиво', name: 'одеколонь', price: 30}},
// // чипсы: {count: 2, good: {_id: 'чипсы', name: 'огурец с молоком', price: 50}},
// //}
// store.dispatch(actionCartDelete({ _id: "пиво", name: "одеколонь", price: 30 }));
// //{чипсы: {count: 2, good: {_id: 'чипсы', name: 'огурец с молоком', price: 50}},
// //}

// store.dispatch(actionCartClear()); // {}

logoutButton.onclick = () => store.dispatch(actionLogout());

store.subscribe(() => {
    const {
        auth: { token, payload },
    } = store.getState();
    if (token) {
        nameDiv.innerHTML = `<a href = "#/dashboard">${payload.sub.login}</a>`;
        authDiv.style.display = "flex";
        noAuthDiv.style.display = "none";
    } else {
        authDiv.style.display = "none";
        noAuthDiv.style.display = "block";
    }
});

store.subscribe(() => {
    const {
        promise: { rootCats },
    } = store.getState();
    if (rootCats?.payload) {
        aside.innerHTML = "";
        for (const { _id, name } of rootCats?.payload) {
            const link = document.createElement("a");
            link.href = `#/category/${_id}`;
            link.innerText = name;
            aside.append(link);
        }
    }
});
const updateCart = () => {
    let {
        cart,
        auth: { token },
    } = store.getState();
    if (!token) {
        return;
    }
    let orderGoods = [];
    Object.entries(cart).map(([index, order]) => {
        orderGoods[orderGoods.length] = { count: order.count, good: { _id: index } };
    });
    console.log(orderGoods);

    main.innerHTML = "";
    let orderList = document.createElement("div");
    let clearButton = document.createElement("button");
    let sum = 0;
    let sumField = document.createElement("div");
    let submitButton = document.createElement("button");

    Object.entries(cart).length ? (submitButton.disabled = false) : (submitButton.disabled = true);

    submitButton.innerText = "Buy";
    submitButton.style.display = "block";
    submitButton.style.marginLeft = "auto";
    submitButton.style.marginTop = "10px";

    sumField.style.marginLeft = "100%";
    sumField.style.fontWeight = "bold";

    clearButton.innerHTML = "Clear cart";
    clearButton.style.display = "block";
    clearButton.style.marginLeft = "auto";

    Object.keys(cart).length > 0 ? (clearButton.disabled = false) : (clearButton.disabled = true);
    Object.keys(cart).length > 0 ? (sumField.style.display = "block") : (sumField.style.display = "none");

    for (let [id, order] of Object.entries(cart)) {
        let { name, images, price } = order.good;
        let orderWrapper = document.createElement("div");
        orderWrapper.style.display = "flex";
        orderWrapper.style.width = "100%";

        let goodBox = document.createElement("div");
        goodBox.style = "display:flex; flex:1;";
        let countBox = document.createElement("div");
        let addButton = document.createElement("button");
        addButton.innerHTML = "+";

        let removeButton = document.createElement("button");
        removeButton.innerHTML = "-";

        let deleteButton = document.createElement("button");
        deleteButton.innerHTML = "X";

        let countField = document.createElement("span");
        countField.innerHTML = order.count;

        countField.innerHTML = order.count;
        goodBox.innerHTML = `
        <div >
            <div><img style="height:100px;" src = "${backendURL}/${images ? images[0].url : ""}"></div>
            <div><h3>${name}</h3></div>
            <div>${order.count} * ${price} = ${+order.count * +price}</div>
        </div>
        `;
        sum += +order.count * +price;
        countBox.append(addButton);
        countBox.append(countField);
        countBox.append(removeButton);
        countBox.append(deleteButton);

        orderWrapper.append(goodBox, countBox);

        addButton.onclick = () => {
            store.dispatch(actionCartChange(order.good, ++order.count));
        };

        removeButton.onclick = () => {
            store.dispatch(actionCartChange(order.good, --order.count));
        };

        deleteButton.onclick = () => {
            store.dispatch(actionCartDelete(order.good));
        };

        orderList.append(orderWrapper);
    }

    clearButton.onclick = () => store.dispatch(actionCartClear());
    submitButton.onclick = () => store.dispatch(actionNewOrder(orderGoods));
    sumField.innerText = sum;
    main.append(orderList);
    main.append(sumField);
    main.append(clearButton);
    main.append(submitButton);
};

window.onhashchange = () => {
    const [, route, _id] = location.hash.split("/");

    const routes = {
        home() {
            main.innerHTML = "Контент";
        },
        category() {
            store.dispatch(actionCatById(_id));
        },
        good() {
            //задиспатчить actionGoodById
            store.dispatch(actionGoodById(_id));
        },
        login() {
            const loginInput = document.createElement("input");
            const passwordInput = document.createElement("input");
            const loginSubmitButton = document.createElement("button");
            loginSubmitButton.innerHTML = "Login";

            main.innerHTML = "";

            main.append(loginInput);
            main.append(passwordInput);
            main.append(loginSubmitButton);

            loginSubmitButton.onclick = () => {
                loginValue = loginInput.value;
                passwordValue = passwordInput.value;
                store.dispatch(actionLogin(loginValue, passwordValue));
            };
        },
        register() {
            const loginInput = document.createElement("input");
            const passwordInput = document.createElement("input");
            const registerSubmitButton = document.createElement("button");
            registerSubmitButton.innerHTML = "Register";

            main.innerHTML = "";

            main.append(loginInput);
            main.append(passwordInput);
            main.append(registerSubmitButton);

            registerSubmitButton.onclick = () => {
                loginValue = loginInput.value;
                passwordValue = passwordInput.value;
                store.dispatch(actionRegister(loginValue, passwordValue));
            };
        },
        async dashboard() {
            await store.dispatch(actionOrders());

            let {
                promise: { orders },
                auth: { token },
            } = store.getState();
            if (!token || orders.status === "REJECTED") {
                return;
            }

            main.innerHTML = "";
            let orderList = document.createElement("div");
            for (let order of orders.payload) {
                let orderBlock = document.createElement("div");
                let orderGoodsBlock = document.createElement("div");
                let orderHeaderBlock = document.createElement("div");
                let orderSumBlock = document.createElement("div");
                let createdAtBlock = document.createElement("div");

                createdAtBlock.innerHTML = ("" + new Date(+order.createdAt)).slice(0, 25);

                for (let orderGood of order.orderGoods) {
                    let {
                        count,
                        good: { name, price, images },
                    } = orderGood;
                    let goodBlock = document.createElement("div");
                    goodBlock.innerHTML = `
                        <div style = "width:30%;">${name}</div><div  style = "width:40%;"><img  src ="${backendURL}/${
                        images[0].url
                    }" style="height:100px"></div><div  style = "width:30%;">${count}x${price} = ${
                        +price * +count
                    }</div>
                    `;
                    goodBlock.style.display = "flex";
                    goodBlock.style.width = "100%";
                    goodBlock.style.marginTop = "7px";
                    goodBlock.style.background = "#EAEAEA";

                    orderGoodsBlock.append(goodBlock);
                }
                orderSumBlock.innerHTML = `<b>TOTAL</b>:${order.total}`;

                orderHeaderBlock.style.padding = "10px";
                orderHeaderBlock.style.display = "flex";
                orderHeaderBlock.style.justifyContent = "space-between";

                orderHeaderBlock.append(createdAtBlock);
                orderHeaderBlock.append(orderSumBlock);

                orderBlock.append(orderHeaderBlock);
                orderBlock.append(orderGoodsBlock);

                orderGoodsBlock.style.marginTop = "10px";
                orderBlock.style.marginTop = "20px";
                orderBlock.style.borderBottom = "1px solid black";
                orderBlock.style.padding = "10px";
                orderList.append(orderBlock);
            }

            main.append(orderList);
        },
        cart() {
            updateCart();
        },
    };

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

store.subscribe(() => {
    let { cart } = store.getState();
    const [, route, _id] = location.hash.split("/");
    if (cart && route === "cart") {
        updateCart();
    }
});
window.onhashchange();

store.subscribe(() => {
    const {
        promise: { catById },
    } = store.getState();
    const [, route, _id] = location.hash.split("/");
    if (catById?.payload && route === "category") {
        const { name } = catById.payload;
        main.innerHTML = `<h1>${name}</h1> ТУТ ДОЛЖНЫ БЫТЬ ПОДКАТЕГОРИИ`;
        for (const { _id, name, price, images } of catById.payload.goods) {
            const card = document.createElement("div");
            card.innerHTML = `<h2>${name}</h2>
                              <img src="${backendURL}/${images[0].url}" />
                              <strong>${price}</strong>
                                <a href="#/good/${_id}">${name}</a>
                                `;
            main.append(card);
        }
    }
});

store.subscribe(() => {
    const {
        promise: { good },
        auth: { token },
    } = store.getState();
    const [, route, _id] = location.hash.split("/");
    if (good?.payload && route === "good") {
        const { name, price, images } = good.payload;

        main.innerHTML = `<h1>${name}</h1>`;

        const card = document.createElement("div");
        card.innerHTML = `<h2>${name}</h2>
                            <img src="${backendURL}/${images[0].url}" />
                            <strong>${price}</strong>
                            `;

        if (token) {
            let buyButton = document.createElement("button");

            buyButton.innerHTML = "Купить";

            buyButton.onclick = () => store.dispatch(actionCartAdd(good.payload));
            card.append(buyButton);
        }
        main.append(card);
    }
});
