/* const getGQL = url => (query,variables={}) => 
    fetch(url,{
    method:"POST",
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    body:JSON.stringify({query,variables})
}).then((res) => res.json())
    .then((data) => {
        if(data.errors && !data.data){
            throw new Error(JSON.stringify(data.errors))
        } else{
            let value = Object.values(data.data);
            return value[0];
        }
    }) */

    //headers:{...{localStorage.authToken & {Autorization: `Bearer ${LocalStorage.authToken} : {}`}}}
/*     const getGQL = url =>{
        return async (query,variables={}) =>{
            let parametrs = {headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
            }}
            let authToken = localStorage.getItem("authToken")
            if(authToken){
                parametrs.headers.Authorization = "Bearer" + authToken
            }
            parametrs.body = JSON.stringify({query,variables})
            parametrs.method = "POST"
            let result = await fetch(url,parametrs)
                .then((res) => res.json())
            if("data" in result) {
                return result.data[Object.keys(result.data)[0]]
            } else if("errors" in result){
                throw new Error(JSON.stringify(result.errors))
            }
        }
    } 
    const backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua'        
    const gql = getGQL('http://shop-roles.asmer.fs.a-level.com.ua/graphql'); */
    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 backendURL = 'http://shop-roles.asmer.fs.a-level.com.ua'

const gql = getGQL(backendURL + '/graphql')
    /* (async()=>{
       let data = await gql(`query login($login:String, $password:String){
           login(login:$login, password:$password)
       }`, {login:'Anon', password:'Anon'});
      
       localStorage.authToken = data;
            
    })(); */
    /* 
     gql(`query login($login:String, $password:String){
        login(login:$login, password:$password)
    }`, {login:'Anon', password:'Anon'})            //login with gql function
        .then((data) => console.log(data)); */
        
/// some queries but works not good
/* query CoutCat{
    CategoryCount(query:"[{}]")
  }
  
  query findCategory {
   CategoryFind(query:"[{}]"){
    name
    goods{name,categories{name}}
    parent{parent{name}}
  }
  }
  
  
  mutation newGood($newGood:GoodInput){
    GoodUpsert(good:$newGood){
      name
      price
      description
      categories{name}
    }
  } */
    function promiseReducer(state={}, {type,name,status,payload,error}){
            /* delay1000:{status, payload,error}
                delay2000: {status, payload,error}*/
                if(!state){
                    delay1000:{status,payload,error}
                    delay2000:{status,payload,error}
                }
                if(type === 'PROMISE'){
                    return{
                        ...state,
                        [name]:{status,payload,error}
                    }
                }
                return state
    }
    
    function createStore(promiseReducer){
        let state       = promiseReducer(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 = promiseReducer(state, action) //пробуем запустить редьюсер
            if (newState !== state){ //проверяем, смог ли редьюсер обработать action
                state = newState //если смог, то обновляем state 
                for (let cb of cbs)  cb() //и запускаем подписчиков
            }
        }
        
        return {
            getState, //добавление функции getState в результирующий объект
            dispatch,
            subscribe //добавление subscribe в объект
        }
    }



    const combineReducers = (reducers) =>
              (state={},action) => {
                  const newState = {}
                for(const [reduceName, reducer] of Object.entries(reducers)){
                        let newSubState = reducer(state[reduceName],action);
                        if(state[reduceName] !== newSubState){
                           newState[reduceName] = newSubState
                        }
                }
                if(Object.keys(newState).length){
                    state = {...state, ...newState}
                }
                return state;
              }
    
    /* let store = createStore(promiseReducer);
    store.subscribe(()=>console.log(store.getState())); */
    const actionPending = name => ({name,type:"PROMISE",status:'PENDING'})
    const actionFulfiled = (name,payload) =>({name,type:"PROMISE",status:'FULFILLED',payload})
    const actionrRejected = (name,error) => ({name,type:"PROMISE",status:'REJECTED',error})
    
    const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
    /* store.subscribe(() => console.log(store.getState())); */
    /* store.dispatch(actionPending('delay1000'))
    
    //{ 1
    //  delay1000: {status: 'PENDING'}
    //}
    delay(1000).then(result => store.dispatch(actionFulfiled('delay1000', result)))
    /* console.log(store.getState()); */
    //{ 3
    //  delay1000: {status: 'FULFILLED', payload: 1000},
    //  delay2000: {status: 'PENDING'}
    //}
    
    /* store.dispatch(actionPending('delay2000'))
    //{ 2
    //  delay1000: {status: 'FULFILLED', payload: 1000},
    //  delay2000: {status: 'PENDING'}
    //}
    delay(2000).then(result => store.dispatch(actionFulfiled('delay2000', result))) */
    //{ 4
    //  delay1000: {status: 'FULFILLED', payload: 1000},
    //  delay2000: {status: 'FULFILLED', payload: 2000}
    //} */
    
    const jwtDecode = token =>{
        try{
            let payload = JSON.parse(atob(token.split('.')[1]));
            return payload;
        } catch(e){
            
        }
    }
    console.log(jwtDecode(localStorage.getItem('authToken')));
    
    function authReducer(state,{type, token}){
        if(state === undefined){
            if(localStorage.authToken){
                type = 'AUTH_LOGIN';
                token = localStorage.authToken
            }
        }
        if(type === 'AUTH_LOGIN'){
            let payload = jwtDecode(token);
            if(payload){
                localStorage.authToken = token;
                return {token,payload}
            }
        }
        if(type === 'AUTH_LOGOUT'){
            localStorage.authToken = '';
            return {};
        }
        return state || {};
    }
    
 function cartReducer(state={},{type, good, count=1}){
        
   /*  _id1: {count:1, good:    {_id1,name,price,images}}
    _id2: {count:1, good:    {_id2,name,price,images}}
    */
     
    if (type === 'CART_ADD'){
       /*  if(good._id in state){
            return{
                ...state,
                [good._id]: {count:state[good._id].count+count,good}
            }
        }else{ */
            return{
                ...state,
                [good._id] : {count: count + (good._id in state ? state[good._id].count : 0),good}
            }
        }
        
    
    if (type === 'CART_CHANGE'){
       /*  let _id1 = good._id;
        console.log(_id1); */
        return{
            ...state,
            [good._id]: {count:count,good:good}
        }
    }
    if (type === 'CART_DELETE'){
       /*  const clone = {...state}
        delete clone[good._id]
        state = {...clone} */
        let {[good._id]: remove, ...newState} = state;
        return newState;
    }
    if (type === 'CART_CLEAN'){
        return {}
    }
    return state
 }   

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 actionCartClean = () => ({type:'CART_CLEAN'})  
    /* let store = createStore(authReducer); */
    /* const store = createStore(promiseReducer); */
const store = createStore(combineReducers({promise: promiseReducer, auth: authReducer,cart: cartReducer}))
/* store.subscribe(() => console.log(store.getState()));
store.dispatch(actionCartAdd({_id:'pivas',name:'cherniga',price:50}));
store.dispatch(actionCartAdd({_id: 'пиво', name: 'одеколонь', price: 30}))
store.dispatch(actionCartAdd({_id: 'пиво', name: 'одеколонь', price: 30},5))
store.dispatch(actionCartChange({_id: 'чипсы', name: 'одеколонь', price: 30}, 2))
store.dispatch(actionCartAdd({_id:'чипсы', name: 'одеколонь', price: 30}))
store.dispatch(actionCartChange({_id: 'pivas', name: 'cherniga', price: 50}, 1))
store.dispatch(actionCartDelete({_id:'pivas',name:'cherniga'})); */
const actionPromise = (name,promise) =>
        async dispatch => {
            dispatch(actionPending(name))
            try{
                let payload = await promise
                dispatch(actionFulfiled(name,payload))
                return payload
            }
            catch(e){
                dispatch(actionrRejected(name,e))
            }
        }
const actionAuthLogin = (tokennn) => ({type:'AUTH_LOGIN',token:tokennn})

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 ($goodId: String){
            GoodFindOne(query:$goodId){
              _id name price images{
                url
              }
              description
            }
          }`,{goodId: JSON.stringify([{_id}])}))
const actionFullRegister = (Login,password) =>
          actionPromise('fullRegister', gql(`mutation register($Login:String,$password:String){
            UserUpsert(user:{login:$Login,password:$password}){
              _id login
            }
          }`,{Login: Login, password: password}))

const actionFullLogin = (Login,password) => async (dispatch) => {
   const tokennn = await dispatch(
    actionPromise('fullLogin', gql(`query login($Login:String,$password:String){
        login(login:$Login,password:$password)
    }`,{Login:Login,password:password}))
   );
   await dispatch(actionAuthLogin(tokennn)); 
}

const actionAllOrders = () => 
        actionPromise('allOrders',gql(`query orders{
            OrderFind(query:"[{}]"){
              _id total
              orderGoods{
                count total good{name,price,
                images{
                  url
                }}
              }
            }
          }`))
const actionCreateOrder = () => async (dispatch) =>{
    let orderGoods = [];
    Object.entries(store.getState().cart).map(([_id,{count}])=>orderGoods.push({"count":count,"good":{_id:_id}}))
    actionPromise('createOrder',gql(`mutation newOrder($order:OrderInput){
        OrderUpsert(order:$order){
          _id total
        }
      }`,{order:{orderGoods}}));
     await store.dispatch(actionCartClean());
}

const actionSearchGood = (good) => 
    actionPromise('searchGood', gql(`query gf($query: String){
        GoodFind(query: $query){
            _id, name, description, price, images{
                _id, url
            }
        }
    }`, {query: JSON.stringify([
                {
                    $or: [{name: `/${good}/`}, {description: `/${good}/`}] //регулярки пишутся в строках
                },
                {
                    sort: [{name: 1}]} //сортируем по title алфавитно
                ])
    }))

store.dispatch(actionRootCats())
    
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)
            }
        }
    })

window.onhashchange = () => {
        const [, route, _id] = location.hash.split('/')
    
        const routes = {
            category(){
                store.dispatch(actionCatById(_id))
            },
            good(){ //задиспатчить actionGoodById
                store.dispatch(actionGoodById(_id)); 
                
            },
            login(){
                //отрисовка тут
               /*  store.subscribe(() => { */
                    const [,route,id] = location.hash.split('/')
                    if(route === 'login'){
                        main.innerHTML = `<h1>Login</h1>`
                        let loginForm = document.createElement('div');
                        loginForm.innerHTML = `<label>login <input id="login"></label>
                                    <label>password <input id="password"></label>
                                    <button id="logButton">Login</button>`
                        main.append(loginForm);
                        logButton.addEventListener('click', function Login(){
                            console.log(login.value, password.value);
                            store.dispatch(actionFullLogin(login.value, password.value))
                            store.getState();
                           /*  store.dispatch(actionAuthLogin(store.getState().promise.fullLogin.payload))
                            store.getState(); */
                        })

                                }
                
                /* }) */
                //по кнопке - store.dispatch(actionFullLogin(login, password))
            },
            register(){
                const [,route,id] = location.hash.split('/');
                if(route === 'register'){
                    main.innerHTML = `<h1>Register</h1>`
                    let registerForm = document.createElement('div');
                    /* registerForm.innerHTML = `<label>login <input id="login"></label>
                                              <label>password <input id="password" type="paasword"></label>
                                              <button id="registerButton">Register</button>` */
                    let labelLog = document.createElement('label');
                    labelLog.innerText = 'login';
                    let login = document.createElement('input');
                    labelLog.append(login);
                    let labelPswd = document.createElement('label');
                    labelPswd.innerText = 'Password';
                    let password = document.createElement('input');
                    password.type = 'password';
                    labelPswd.append(password);
                    let registerButton = document.createElement('button');
                    registerButton.innerText = 'Register';
                    registerForm.append(labelLog);
                    registerForm.append(labelPswd);
                    registerForm.append(registerButton);                         
                    main.append(registerForm);
                    registerButton.addEventListener('click', function register(){
                        console.log(login.value, password.value);
                        store.dispatch(actionFullRegister(login.value, password.value))
                    })
                }
                //отрисовка тут
                //по кнопке - store.dispatch(actionFullRegister(login, password))
            },
            dashboard(){
                store.dispatch(actionAllOrders());
            },
            cart(){
                const[,route,id] = location.hash.split('/')
                const cart = store.getState().cart;
                if(route === 'cart'){
                    main.innerHTML = `<h1>Cart</h1>`
                    for(let good in cart){
                   /*  goods.innerHTML = `<img src="${backendURL}/${cart[good].good.images[0].url}">
                                        <p>${cart[good].good.name}</p>
                                        <input type="number" value=${cart[good].count} min="0" id="count">
                                        <button id="plusCount">+</button> <button id="minusCount">-</button>
                    
                    ` */
                    let goods = document.createElement('div');
                    let img = document.createElement('img');
                    img.src = `${backendURL}/${cart[good].good.images[0].url}`;
                    let name = document.createElement('p');
                    name.innerText = `${cart[good].good.name}`
                    let count = document.createElement('input');
                    count.type = 'number';
                    count.min = 1; 
                    count.value = cart[good].count;
                    let countPlus = document.createElement('button');
                    countPlus.innerText = '+'
                    let countMinus = document.createElement('button');
                    countMinus.innerText = '-';
                    countPlus.onclick = () => {
                        cart[good].count++;
                        count.value = cart[good].count;
                        store.dispatch(actionCartChange(cart[good].good, +count.value))
                    }
                    countMinus.onclick = () =>{
                        cart[good].count--;
                        count.value = cart[good].count;
                        if(cart[good].count < 1){
                            count.value = 1;
                            cart[good].count = 1;
                            store.dispatch(actionCartChange(cart[good].good, +count.value))
                        }
                        store.dispatch(actionCartChange(cart[good].good, +count.value))
                    }
                    count.oninput = () => {
                        store.dispatch(actionCartChange(cart[good].good, +count.value))
                    }
                    let removeBtn = document.createElement('button');
                    removeBtn.innerText = 'Remove';
                    removeBtn.onclick = () => {
                        store.dispatch(actionCartDelete(cart[good].good))
                        window.onhashchange()
                        cartIcon.innerHTML = `Cart ${cart[good].count}`
                    }
                    goods.append(img);
                    goods.append(name);
                    goods.append(count);
                    goods.append(countPlus);
                    goods.append(countMinus);
                    goods.append(removeBtn);
                    main.append(goods);
                
                }
                let orderButton = document.createElement('button');
                orderButton.innerText = 'Order';
                orderButton.onclick = () => {
                    store.dispatch(actionCreateOrder());
                    window.onhashchange()
                    cartIcon.innerHTML = `Cart`
            }
                if(localStorage.authToken && Object.keys(cart).length !== 0){
                    orderButton.disabled = ""
                } else {
                    orderButton.disabled = "true"
                }
                main.append(orderButton);
                }
              
            }
        }
        if (route in routes)
            routes[route]()
    }
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> TYT PODCATEGORII`;
        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}UAH</strong>
                            <a href="#/good/${_id}">ссылка на товарчик</a>
                            <button id="buyGood">Купить</button>
            
            ` */   
          let named = document.createElement('h2');
          named.innerText = `${name}`;
          let imaged = document.createElement('img');
          imaged.src = `${backendURL}/${images[0].url}`;
          let priced = document.createElement('strong');
          priced.innerText = `${price}`;
          let link = document.createElement('a');
          link.href = `#/good/${_id}`;
          link.innerText = 'ссылка на товарчик';
          let buttonBuy = document.createElement('button');
          buttonBuy.innerText = 'Купить';
          buttonBuy.onclick = () => {
              store.dispatch(actionGoodById(_id));
              store.dispatch(actionCartAdd({_id,name,price,images}));   
          }
          card.append(named);
          card.append(imaged);
          card.append(priced);
          card.append(link);
          card.append(buttonBuy);

          main.append(card);
        }
    }
})
store.subscribe(() => {
    const {promise: {goodById}} = store.getState();
    const [,route,id] = location.hash.split('/')
    if(goodById?.payload && route === 'good'){
        const {name,price,description,images} = goodById.payload;
        main.innerHTML = `<h1>${name}</h1>`
            const card = document.createElement('div');
            card.innerHTML = `<h2>${name}</h2>
                              <img src="${backendURL}/${images[0].url}">
                              <p>${description}</p>
                              <strong>${price}UAH</strong>
                              <button id="buyGood">Купить</button>  
            `
        main.append(card);
         let buyGood = document.getElementById('buyGood');
        buyGood.addEventListener('click', function addToCart(){
            console.log(id);
            store.dispatch(actionCartAdd(store.getState().promise.goodById.payload));
        }) 
    }
})
store.subscribe(() => {
    let counts = 0;
    const cartIcon = document.getElementById('cartIcon');
    const orders = store.getState().cart;
    for (let order in orders){
        counts += orders[order].count
        cartIcon.innerHTML = `<a href="#/cart">Cart ${counts}</a>`
        /* if(!orders[order].count){
            cartIcon.innerHTML = `Cart ${counts}`
        } */
    }

})
store.subscribe(async() => {
                const[,route,id] = location.hash.split('/')
                const {promise: {allOrders}} = await store.getState();
                if(route === 'dashboard' && allOrders.payload){
                    main.innerHTML = `<h1>Dashboard</h1>`
                    for(const{_id,total,orderGoods} of allOrders.payload){
                       let order = document.createElement('div');
                       let idOfOrder = document.createElement('h2');
                       idOfOrder.innerText = `${_id}`;
                       let totalCost = document.createElement('label');
                       totalCost.innerText = `${total}`
                       order.append(idOfOrder);
                       order.append(totalCost);
                       for(const{count,total,good} of orderGoods){
                           let card = document.createElement('div');
                           card.innerHTML = `<h2 id="name">${good.name}</h2>
                                            <p id="counted">${count}</p>
                                            <p id="totaled">${total}</p>
                                            <img src="${backendURL}/${good.images[0].url}"> 
                           `
                           order.append(card);
                       }
                       
                       
                        main.append(order);         
                    }
                }
})
let inputSearch = document.createElement('input');
inputSearch.type = "text";
let header = document.getElementById('header');
header.append(inputSearch);
let divWithGood = document.createElement('div');
divWithGood.style.display = 'none';
header.append(divWithGood); 
function render(parent,data){
    console.log(data);
}
store.subscribe(() => {
    let re = /\s* \s*/;
    inputSearch.oninput = () => {
            let newStr = inputSearch.value.trim().split(re).join("|");
            store.dispatch(actionSearchGood(newStr));
        }   
    inputSearch.onfocus = () => {
        divWithGood.style.display = 'block';
        const searchGood = store.getState().promise.searchGood;
        render(divWithGood,searchGood);

    }
    inputSearch.blur = () => {
        divWithGood.style.display = 'none';
        render(divWithGood,searchGood);
    }


})
    let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWJiNjNjOWM3NTBjMTJiYTZiYTRiMzgiLCJsb2dpbiI6IkFub24iLCJhY2wiOlsiNjFiYjYzYzljNzUwYzEyYmE2YmE0YjM4IiwidXNlciJdfSwiaWF0IjoxNjQwMjAwNTU2fQ.P_JXFko7OFObxVn8uB6TR3R1V9aB1MMQrgX58MpEq5E';
    /* store.dispatch(actionPromise('delay1000', delay(1000)))
    store.dispatch(actionPromise('delay2000', delay(2000))) */
    
    // store.dispatch(actionAuthLogin(token))
    const actionAuthLogout= () => ({type: 'AUTH_LOGOUT'})
    // store.dispatch(actionAuthLogout());
    /* console.log(store.getState()); */
    /* const actionRegister = (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(actionRegister('aufff', '123456')); */
    
    
    /* const combinedStore = createStore(combineReducers({promise: promiseReducer, auth: authReducer})) */

 //search
//  let re = /\s* \s*/;
// let input = document.createElement('input');
// input.type = 'text';
// document.body.append(input);
// input.oninput = () => {
//     let newStr = input.value.trim().split(re).join("|");
// console.log(`/${newStr}/`);
// }  