<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>authReducer</title>
	<style>
        #mainContainer{
			display:flex;
		}
		#aside{
			width: 30%;
		}
		#aside > a{
			display: block;
		}

		img{
			width:300px;
		}
		main{
			padding-left: 20px;
		}
		table{
			border:1px solid black;
		}
		td{
			text-align: center;
			border:1px solid black;
		}
    </style>
</head>
<body>

	<header>
            <div id="formId"></div>
            <div id="ownCab"></div>
        </header>
        <div id='mainContainer'>
            <aside id='aside'>
                Категории
            </aside>
            <main id='main'>
                Контент
            </main>
        </div>

        

	<input type="submit" value="click 1" id="btn1">
	<input type="submit" value="click 2" id="btn2">

	<script>
		
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 
    }
}

//----------------------------------------------------------------
		//написать jwtDecode = token =>({})
		const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZTA1MmM3NTBjMTJiYTZiYTQwMjkiLCJsb2dpbiI6InZsYWRCcmF1bjQiLCJhY2wiOlsiNjFhNGUwNTJjNzUwYzEyYmE2YmE0MDI5IiwidXNlciJdfSwiaWF0IjoxNjM4NTM5NTUzfQ.oPRus9nGS1rg69eKu8rK-tMi4V-hN5HXE0NOzAc5K4k";

		//выкусить из токена серединку 
		//сделать base64 декод (atob)
		//с результатом сделать JSON.parse
		const jwtDecode = token =>{
			try{
				let mid=token.split('.');
				let tok=mid[1];
				let tokenDecode=atob(tok);
				let finalTok=JSON.parse(tokenDecode);
				return finalTok;
			}catch(e){
				console.log(e);
			}
		}
		console.log(jwtDecode(token));

//------------------------------------------------------------
	function authReducer(state={},{type, token}){
		if(!state){
			if(localStorage.authToken){
				type="AUTH_LOGIN";
				token=localStorage.authToken;
			}else{
				return {};
			}
		}
		if(type==='AUTH_LOGIN'){
			//сохранить в localStorage token
			//вернуть {token, payload: jwtDecode}
			//если payload не обьект, то вернуть {}
			//вернуть {token, payload}
			localStorage.authToken=token;
			let payload=jwtDecode(token);
			if(typeof payload==='object'){
				return {
					token,
					payload
				}
			}else{
				return {};
			}
		}
		if(type==='AUTH_LOGOUT'){
			//удалить из localStorage token и вернуть {}
			delete localStorage.authToken;
			return {};
		}
		return state
	}

	const actionAuthLogin = token => ({type:'AUTH_LOGIN', token})
	const actionAuthLogout = () => ({type:'AUTH_LOGOUT'})

//---------------------------------------------------------------
	function combineReducers(reducers){
		return (state={},action)=>{
			let newState={};
			
			for(const [reducerName, reducer] of Object.entries(reducers)){
				let newSubState=reducer(state[reducerName],action);
				if(newSubState!==state[reducerName]){
					newState[reducerName]=newSubState;
				}
			}
			if(0 !==Object.keys(newState).length){
				return{
					...state,
					...newState
				}
			}else{
				return state;
			}
			//перебрать все редьюсеры
				//запустить каждый из них
				//передать при этом в него его Ветвь общего state и экшен как есть
				//получить newSubState
				//если newSubState отлиается от входящего, то записать newSubState в newState 
			//после цикла, если newState не пуст, то вернуть {...state, ...newState}
			//иначе вернуть old state
		}
	}

//-------------------------------------------------------------------------------
	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 payload = await promise
	      dispatch(actionResolved(name, payload))
	      return payload
	    }
	    catch (error) {
	      dispatch(actionRejected(name, error))
	    }
	  }

//------------------------------------------------------------------------------------------
	const combinedReducer= combineReducers({promise:promiseReducer,auth:authReducer});
	const store=createStore(combinedReducer)
	console.log(store.getState());
	//const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
	//store.dispatch(actionPromise('delay1000',delay(1000)))//{promise:{delay1000:''},auth:{}}
	//store.dispatch(actionAuthLogin(token))//{promise:{delay1000:''},auth:{token...}}
	

	/*let store=createStore(authReducer);
	store.subscribe(() => console.log(store.getState()));*/

	btn1.onclick = () => store.dispatch(actionAuthLogin(token));
	btn2.onclick = () => store.dispatch(actionAuthLogout());

	/*const actionLogin = (login,password) =>
		actionPromise('catById', gql('ЗАПРОС НА ЛОГИН', {login, password}))

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

//----------------------------------------------------------------------------------
// const actionRegister // actionPromise
// const actionFullLogin = (login, pasword) => //actionRegister + actionFullLogin
// + интерфейс к этому - форму логина, регистрации, может повесить это на #/login #/register
// + #/orders показывает Ваши бывшие заказы 
// сделать actionMyOrders

//-----------------------------------------------------------------------------------
    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 && !data.data)
			throw new Error(JSON.stringify(data.errors))
		return data.data[Object.keys(data.data)[0]]
	})

	const backURL='http://shop-roles.asmer.fs.a-level.com.ua'
	const gql=getGQL(backURL+'/graphql')

//------------------------------------------Actions------------------------------------------------
	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 registration($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 log = await dispatch(actionRegister(login, password))
	      if (log) {
	        let token = await dispatch(actionLogin(login, password))
	        if (token){
	            dispatch(actionAuthLogin(token))
	        }
	    }
	}
	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 description images {
	        url
	      }
	    }
	    subCategories {
	      name _id goods {
	        _id name description
	      }
	    }
	  }
	}`, { 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 }]) }))

	store.dispatch(actionRootCats())

//--------------------front-------------------------------------------------
	store.subscribe(() => {
	  	const {promise} = store.getState()
	  	console.log('------------')
	  	console.log(promise)
	  	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)
	    	}
	  	}
	})

	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 (let {_id, name} of promise.catById.payload.subCategories) {
	        		const podCat = document.createElement('a')
	        		podCat.className = 'sub_cat'
	        		podCat.href = `#/category/${_id}`
	        		podCat.textContent = name
	       	 		main.append(podCat)
	      		}
	    	}
	    	for (const { _id, name, price, images } of promise.catById.payload.goods) {
	      		const card = document.createElement('div')
	      		card.innerHTML = `<h2>${name}</h2>
				            <img src="${backURL}/${images[0].url}"/>
				                <div>
				                    <b>Стоимость:</b> <b><sub>${price}UAH</sub></b>
				                    <br><a href=#/good/${_id}>Страница товара</a>
				                </div>`
	      		main.append(card)
	    	}
	  	}
	})
//-------------------opis tovara-------------------------------
	store.subscribe(() => {
	  	const {promise} = store.getState()
	 	const [, route, _id] = location.hash.split('/')
	  	if (promise?.goodById?.payload && route === 'good' && location.href.includes(`#/good/${_id}`)) {
	    	main.innerHTML = ``
	    	let {_id, name, price, images, description} = promise.goodById.payload
	      		let item = document.createElement('div')
	      		item.className = 'good_item'
	     		item.innerHTML =`<h2>${name}</h2>
				    <img src="${backURL}/${images[0].url}"/>
				    <div>
				       <p><b>Стоимость:</b> <b><sub>${price} UAH</sub></b></strong></p>
				       <p><b>Описание:</b> <sub>${description}</sub></p>
				    </div>`
	      	main.append(item)
	  	}
	})

//-----------------Orders------------------------------------------------
	const ActionMyOrders = () =>
	actionPromise('orderfind', gql(`query orderfind{
	    OrderFindOne(query:"[{}]"){
	    	_id createdAt total orderGoods{
	      		_id createdAt price count good{
	        		_id name description images{
	          			_id url
	        		}
	      		}
	    	}
	  	}
	}`))

	store.subscribe(() => {
	  	const {promise} = store.getState()
	  	const [,route, _id] = location.hash.split('/')
	 	if (promise?.orderfind?.payload && route === 'orders'){
	      	let counT = 0
	      	main.innerHTML = ''
	      	const {total, orderGoods} = promise.orderfind.payload
	      	let title = document.createElement('h2')
	      	title.textContent = 'Ваши заказы';
	      	title.className = 'title'
	      	let table = document.createElement('table')
	      	let tr = document.createElement('thead')
	      	tr.innerHTML = `<th>Дата заказа</th><th>Название</th><th>Количество</th><th>Цена</th>`
	      	table.append(tr)
	      	for (let {createdAt, count, good, price} of orderGoods){
	          	counT += count;
	          	let date = new Date(+createdAt).toLocaleDateString();
	          	let tr = document.createElement('tr')
	          	tr.innerHTML = `<td>${date}</td>
	          		<td><figure><img src="${backURL}/${good.images[0].url}"><figcaption>${good.name}</figcaption></figure></td>
	          		<td>${count}</td><td>${price}</td>`
	          	table.append(tr)
	      	}
	      	let tr2 = document.createElement('tr')
	      	tr2.innerHTML = `<th>Всего товаров в заказе: ${counT}</th><th>Общая сумма заказа: ${total}</th>`
	      	table.append(tr2)
	      	main.append(title, table)
	  	}else if(!promise?.orderfind?.payload && route === 'orders'){
	      	main.innerHTML = ''
	      	let title = document.createElement('h2')
	      	title.textContent = 'Вы еще не сделали заказ';
	      	main.append(title)
	  	}
	})

//-------------------Login-------------------------------------------------
	store.subscribe(() => {
	  	const {auth} = store.getState()
	  	const {payload} = auth
	  	if (payload?.sub ) {
	      	ownCab.innerHTML = ''
	      	ownCab.style.marginTop=10+'px'
	      	ownCab.style.border=5+'px solid blue'
	      	ownCab.style.width=17+'%'
	      	const {id, login}  = payload.sub
	      	const userName = document.createElement('div')
	      	userName.innerHTML = `Hello, ${login}`
	      	const userOrders = document.createElement('a')
	      	userOrders.innerText = 'Your Orders'
	      	userOrders.style.marginRight=10+'px'
	      	userOrders.href = `#/orders/`
	      	let logout = document.createElement('button')
	      	logout.textContent = 'Exit'
	      	logout.onclick = () => {
	          	formId.innerHTML = ''
	          	store.dispatch(actionAuthLogout());
	      	}
	      	ownCab.append(userName, userOrders, logout)
	  	}else{
	    	ownCab.innerHTML = ''
	  	}
	})

	store.subscribe(() => {
	  	const {auth} = store.getState()
	  	if (!auth?.payload){
	      	formId.innerHTML = '';
	      	let loginBtn = document.createElement('a');
	      	loginBtn.style.border=1+'px solid green'
	      	let regBtn = document.createElement('a');
	      	formId.append(loginBtn, regBtn)
	      	loginBtn.textContent = 'LogIn'
	      	regBtn.style.marginLeft=10+'px'
	      	regBtn.style.border=1+'px solid yellow'
	      	regBtn.textContent = 'Registration'
	      	loginBtn.onclick = () => {
	          	loginBtn.style.display = 'none'
	          	regBtn.style.display= 'none'
	          	let form = document.createElement('form')
	          	let login = document.createElement('input')
	          	login.type = 'text'
	          	login.placeholder = 'Login'
	          	let password = document.createElement('input')
	          	password.placeholder = 'Password'
	          	password.type = 'password'
	          	let button = document.createElement('a')
	          	button.textContent = 'Enter'
	          	button.onclick = () => {
	              	if (login.value !== '' && password.value !== '') {
	                  	button.href = `#/login/${login.value}*${password.value}`
	              	}
	          	}
	          	form.append(login, password, button)
	          	formId.append(form)         
	      	}
	      	regBtn.onclick = () => {
	          	loginBtn.style.display = 'none'
	          	regBtn.style.display= 'none'
	          	let form = document.createElement('form')
	          	let login = document.createElement('input')
	          	login.type = 'text'
	          	login.placeholder = 'Login'
	          	let password = document.createElement('input')
	          	password.placeholder = 'Password'
	          	password.type = 'password'
	          	let button = document.createElement('a')
	          	button.textContent = 'Registration'
	          	button.onclick = () => {
	              	if (login.value !== '' && password.value !== '') {
	                  	button.href = `#/register/${login.value}*${password.value}`
	              	}
	          	}
	          	form.append(login, password, button)
	          	formId.append(form)
	      	}
	  	}
	})

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

	  const routes = {
	    category() {
	      store.dispatch(actionCatById(_id))
	    },
	    good() {
	      store.dispatch(actionGoodById(_id)) 
	      //задиспатчить actionGoodById
	      console.log('ТОВАРОСТРАНИЦА')
	    },
	    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(ActionMyOrders())
	    }
	  }
	  if (route in routes)
	    routes[route]()
	}
	window.onhashchange()
	</script>
</body>
</html>