//********REDUX*********
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 в объект
    }
}
const jwtDecode = token => {
    console.log(token)
    try {
        return JSON.parse(atob(token.split('.')[1]))
    }
    catch (e){
        console.log("ERROR" , e.message)
    }
}
function authReducer(state, {type, token}) {
    if (!state){
        if (localStorage.authToken) {
            type = 'AUTH_LOGIN'
            token = localStorage.authToken
        }
        else {
            return {}
        }
    }
    if (type === 'AUTH_LOGIN'){
        let payload = jwtDecode(token)
        if(payload){
            localStorage.authToken = token
            return {
                ...state,
                token,
                payload
            }
        }
        return {}
    }
    if (type === 'AUTH_LOGOUT'){
        delete localStorage.authToken
        return {}
    }
    return state
}
const actionAuthLogin = token => ({type: 'AUTH_LOGIN', token})
const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})

function promiseReducer(state={}, {type, status, payload, error, name}){
    if (type === 'PROMISE'){
        return {
            ...state,
            [name]:{status, payload, error}
        }
    }
    return state;
}
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 actionPromise = (name, promise) =>
    async dispatch => {
        dispatch(actionPending(name))
        try {
            let data = await promise
            dispatch(actionResolved(name, data))
            return data
        }
        catch(error){
            dispatch(actionRejected(name, error))
        }
    }

function combineReducers(reducers) {
    return (state={}, action) => {
        const newState = {}
        for (const [reducerName, reducer] of Object.entries(reducers)) {
            const newSubState = reducer(state[reducerName], action)
            if(newSubState !== state[reducerName]) {
                newState[reducerName] = newSubState
            }
        }
        if (Object.entries(newState).length !== 0){
            return {...state, ...newState}
        }
        else{
            return state
        }
    }
}
const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer})

const store = createStore(combinedReducer)
store.subscribe(() => console.log(store.getState()))
//const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms));
//store.dispatch(actionPromise('delay1000', delay(1000)))
//store.dispatch(actionAuthLogin(token))
//*****************************************************************************


const getGQL = url =>
    async (query, variables={}) => {
        let obj = await fetch(url, {
            method: 'POST',
            headers: localStorage.authToken ? {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + localStorage.authToken
            } : {"Content-Type": "application/json"},
            body: JSON.stringify({ query, variables })
        })
        let a = await obj.json()
        if (!a.data && a.errors)
            throw new Error(JSON.stringify(a.errors))
        return a.data[Object.keys(a.data)[0]]
    }
const backURL = 'http://shop-roles.asmer.fs.a-level.com.ua'
const gql = getGQL(backURL + '/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
              }
            },
            subCategories{
              _id name, subCategories{
                name
              }
            }
          }
    }`, {q: JSON.stringify([{_id}])}))

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

// отрисовка заказов
const actionOrderFindOne = () =>
    actionPromise('orderfind', gql(`query orderfind{
         OrderFindOne(query:"[{}]"){
        _id createdAt total orderGoods{
          _id createdAt price count good{
            _id name description images{
              _id url
            }
          }
        }
      }
    }`))

store.dispatch(actionRootCats())
store.subscribe(() => {
    const {promise} = store.getState()
    if (promise?.rootCats?.payload){
        aside.innerHTML = ''
        for (const {_id, name} of promise?.rootCats?.payload){
            const link      = document.createElement('a')
            link.href       = `#/category/${_id}`
            link.innerText  = name
            aside.append(link)
        }
        // отрисовка заказов
        get.innerHTML = ''
        let btnGet = document.createElement('a');
        btnGet.textContent = 'order find'
        btnGet.href       = '#/orders/'
        get.append(btnGet)
    }
})
store.subscribe(() => {
    const {promise} = store.getState()
    const [,route, _id] = location.hash.split('/')
    if (promise?.catById?.payload && route === 'category'){
        const {name} = promise.catById.payload
        main.innerHTML = `<h1>${name}</h1>`
        if(promise.catById.payload?.subCategories){
            for (const {_id , name} of promise.catById.payload.subCategories) {
                const link      = document.createElement('a')
                link.href       = `#/category/${_id}`
                link.textContent = name
                main.append(link)
            }
        }
        for (const {_id, name, price, images} of promise.catById.payload.goods){
            const card      = document.createElement('div')
            card.className = 'item--main'
            card.innerHTML = `<h2>${name}</h2>
                              <img src="${backURL}/${images[0].url}" />
                              <strong>Цена: ${price} USD</strong><br>
                              <a href="#/good/${_id}">Ссылка на страницу товара: #/good/${_id}</a>`
            main.append(card)
        }
    }
})
store.subscribe(() => {
    const {promise} = store.getState()
    const [,route, _id] = location.hash.split('/')
    if (promise?.goodById?.payload && route === 'good' && window.location.href.includes(`#/good/${_id}`)){
        const {_id ,name, images, price, description } = promise.goodById.payload
        main.innerHTML = ''
        let div = document.createElement('div')
        div.className = 'item--main'
        div.innerHTML = `<h1>${name}</h1>
                         <img src="${backURL}/${images[0].url}" />
                         <p><strong>Описание:</strong> <br> ${description}</p>
                         <p><strong>ID:</strong> ${_id}</p>
                         <strong>Цена: ${price} USD</strong>`
        main.append(div);
    }
})
//*****************************************************************************

//Отрисовка страницы с формой авторизации
//const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZGFjZGM3NTBjMTJiYTZiYTQwMTUiLCJsb2dpbiI6ImFsZXhyZXpuaWNoZW5rbyIsImFjbCI6WyI2MWE0ZGFjZGM3NTBjMTJiYTZiYTQwMTUiLCJ1c2VyIl19LCJpYXQiOjE2MzgxOTQxMDN9.HCRoXBSLYcswzfYk5kPmtN9cx-7XYBRZzi-WDt_kNQk';
//const login = "alexreznichenko"
//const password =  "alexreznichenko"

const actionLogin = (login, password) =>
    actionPromise('login', gql(`query login($login: String, $password: String){
      login(login: $login, password: $password)
    }`, {login: login, password: password}))
const actionFullLogin = (login, password) =>
    async dispatch => {
    let token = await dispatch(actionLogin(login, password))
       if (token){
           dispatch(actionAuthLogin(token))
       }
    }

const actionRegister = (login, password) =>
    actionPromise('register', gql(`mutation register($login:String, $password: String){
      UserUpsert(user:{
                 login: $login, 
                 password: $password, 
                 nick: $login}){
        _id login
      }
    }`, {login: login, password: password}))
const actionFullRegister = (login, password) =>
    async dispatch => {
        let allow = await dispatch(actionRegister(login, password))
        if (allow) {
            let token = await dispatch(actionLogin(login, password))
            if (token){
                console.log('okay')
                dispatch(actionAuthLogin(token))
            }
        }
    }

// создание кнопок авторизации
store.subscribe(() => {
    const {auth} = store.getState()
    if (!auth?.payload){
        wrapper.innerHTML = '';
        let signIn = document.createElement('a');
        let signUp = document.createElement('a');
        signIn.textContent = 'Sign in'
        signUp.textContent = 'Sign up'
        signIn.onclick = () => {
            signIn.style.display = 'none'
            signUp.style.display = 'none'
            let form = document.createElement('from')
            let logInp = document.createElement('input')
            let passInp = document.createElement('input')
            let btnReg = document.createElement('a')
            logInp.placeholder = 'login'
            logInp.type = 'text'
            passInp.placeholder = 'password'
            passInp.type = 'password'
            btnReg.textContent = 'Log In'
            btnReg.onclick = () => {
                if (logInp.value !== '' && passInp.value !== '') {
                    btnReg.href = `#/login/${logInp.value}*${passInp.value}`
                }
            }
            form.append(logInp, passInp, btnReg)
            wrapper.append(form)
        }
        signUp.onclick = () => {
            signIn.style.display = 'none'
            signUp.style.display = 'none'
            let form = document.createElement('from')
            let logInp = document.createElement('input')
            let passInp = document.createElement('input')
            let btnReg = document.createElement('a')
            logInp.placeholder = 'login'
            logInp.type = 'text'
            passInp.placeholder = 'password'
            passInp.type = 'password'
            btnReg.textContent = 'Registration'
            btnReg.onclick = () => {
                if (logInp.value !== '' && passInp.value !== '') {
                    btnReg.href = `#/register/${logInp.value}*${passInp.value}`
                }
            }
            form.append(logInp, passInp, btnReg)
            wrapper.append(form)
        }
        wrapper.append(signIn, signUp)
    }
})
// отрисовка пользователя
store.subscribe(() => {
    const {auth} = store.getState()
    if (auth?.payload){
        wrapper.innerHTML = ''
        let {iat} = auth.payload
        let {id, login} = auth.payload.sub
        let date = new Date(iat * 1000);
        let hours = date.getHours();
        let minutes = "0" + date.getMinutes();
        let seconds = "0" + date.getSeconds();
        let formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);

        let allow = document.createElement('div');
        allow.style.display = 'flex'
        allow.style.flexDirection = 'column'
        allow.innerHTML = `<strong>Вы успешно авторизированны</strong>
                           <strong>Ваш логин: ${login}</strong>
                           <strong>Ваш ID: ${id}</strong>
                           <strong>Время авторизации: ${formattedTime}</strong>`
        let btnLogout = document.createElement('button')
        btnLogout.textContent = 'LOGOUT'
        btnLogout.onclick = () => {
            wrapper.innerHTML = ''
            store.dispatch(actionAuthLogout());
        }
        wrapper.append(allow, btnLogout)
    }
})
// отрисовка всех заказов
store.subscribe(() => {
    const {promise} = store.getState()
    const [,route, _id] = location.hash.split('/')
    if (promise?.orderfind?.payload && route === 'orders'){
        let counts = 0
        let amound = 0
        main.innerHTML = ''
        const {createdAt, total, orderGoods} = promise.orderfind.payload
        let h = document.createElement('h1')
        h.textContent = 'Your orders';
        let table = document.createElement('table')
        let tr = document.createElement('thead')
        tr.innerHTML = `<th>Номер заказа</th><th>Время добавления заказа</th><th>Количество</th><th>Товар</th><th>Цена</th>`
        table.append(tr)
        for (let {createdAt, count, good, price} of orderGoods){
            amound += count;
            let date = new Date(+createdAt);
            let hours = date.getHours();
            let minutes = "0" + date.getMinutes();
            let seconds = "0" + date.getSeconds();
            let formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
            let tr = document.createElement('tr')
            let td = document.createElement('td')

            tr.innerHTML = `<td>${counts++}</td><td>${formattedTime}</td><td>${count}</td>
            <td class="table-order-td"><span>${good.name}</span><img src="${backURL}/${good.images[0].url}"></td><td>${price}</td>`
            table.append(tr)
        }
        let tr2 = document.createElement('tr')
        let date = new Date(+createdAt);
        let hours = date.getHours();
        let minutes = "0" + date.getMinutes();
        let seconds = "0" + date.getSeconds();
        let formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
        tr2.innerHTML = `<th></th><th>${formattedTime}</th><th>${amound}</th><th></th><th>${total}</th>`
        table.append(tr2)
        main.append(h, table)
    }
    else if (!promise?.orderfind?.payload && route === 'orders'){
        main.innerHTML = ''
        let h = document.createElement('h1')
        h.textContent = 'У вас ещё нет заказов';
        main.append(h)
    }
})

window.onhashchange = () => {
    const [,route, _id] = location.hash.split('/')
    const routes = {
        category(){
            store.dispatch(actionCatById(_id))
        },
        good(){
            store.dispatch(actionGoodById(_id))
        },
        login(){
            let data = _id.split('*')
            store.dispatch(actionFullLogin(data[0], data[1]))
        },
        register(){
            let data = _id.split('*')
            store.dispatch(actionFullRegister(data[0], data[1]))
        },
        orders(){
            store.dispatch(actionOrderFindOne())
        }
    }
    if (route in routes)
        routes[route]()
}
window.onhashchange()