import { useEffect, useState } from 'react';
import logo from './logo.svg';
import './App.scss';
import thunk from 'redux-thunk';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {Provider, connect} from 'react-redux';
import { Link, Route, Router, Switch, Redirect } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'

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)) // 1. {delay1000: {status: 'PENDING'}}
        try{
            let payload = await promise
            dispatch(actionResolved(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 && !data.data)
               throw new Error(JSON.stringify(data.errors))
            return data.data[Object.keys(data.data)[0]]
         })

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

function jwtDecode(token) {
    try {
        let decoded = token.split('.')
        decoded = decoded[1]
        decoded = atob(decoded)
        decoded = JSON.parse(decoded)
        return decoded

    } catch (e) {
        return;
    }
}

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


const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', good, count })
const actionCartRemove = (good, count = 1) => ({ type: 'CART_REMOVE', good, count })
const actionCartChange = (good, count = 1) => ({ type: 'CART_CHANGE', good, count })
const actionCartClear = (good, count = 1) => ({ type: 'CART_CLEAR', good, count })

function cartReducer(state = {}, { type, good={}, count=1}){
    const {_id } = good
    const types = {
        CART_ADD() {
            return {
                ...state,
                [_id]: { good, count: count + (state[_id]?.count || 0)}
            }
        },
        CART_REMOVE(){ 
            let newState = { ...state }
            delete newState[_id]
            return {
                ...newState
            }            
        },
        CART_CHANGE(){
            return {
                ...state, 
                    [_id]:{good, count}
            }
        },
        CART_CLEAR(){
            return {}
        },
    }

    if (type in types)
        return types[type]()


    return state
}

function authReducer(state, {type, token}){
    if (!state) {
        if (localStorage.authToken) {
            type = 'AUTH_LOGIN'
            token = localStorage.authToken
        } else {
            return {}
        }
    }
    if (type === 'AUTH_LOGIN') {
        let auth = jwtDecode(token)
        if (auth) {
            localStorage.authToken = token
            return { token, payload: auth }
        }
    }
    if (type === 'AUTH_LOGOUT') {
        localStorage.authToken = ''
        return {}
    }

    return state
}

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

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

const actionLogin = (login, password) =>
    actionPromise('login', gql(`
    query log($login:String, $password:String) {
        login(login: $login, password: $password)
      }`, { login, password }))

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

const actionRegister = (login, password) =>
    actionPromise('registration', gql(`
        mutation register($login:String, $password:String) {
            UserUpsert(
            user: {
                login: $login,
                password: $password,
            }){
                login _id 
            }
        }
      `, { login, password }))

const actionFullRegister = (login = 'tst', password = '123') =>
    async dispatch => {
        await dispatch(actionRegister(login, password))
        await dispatch(actionFullLogin(login, password))
    }

const actionCatById = (_id) =>
    actionPromise('catById', gql(`query ($q: String){
        CategoryFindOne(query: $q){
            _id name goods {
                _id name price images {
                    url
                }
            } 
            subCategories {
                _id name
            }
        }
    }`, { q: JSON.stringify([{ _id }]) }))

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


const store = createStore(combineReducers({promise: promiseReducer, 
                                            auth: authReducer, 
                                            cart: cartReducer}), applyMiddleware(thunk)) 
store.subscribe(() => console.log(store.getState()))
store.dispatch(actionRootCats())


const Logo = () =>
    <Link to="/">
        <img src={logo} className="Logo" alt="logo" />
    </Link>

const Header = () =>
    <header>
        <Logo />
    </header>

const Navbar = () =>
    <nav className='Navbar'>
        <CKoshik/>
        <LogBtn />
        <RegBtn />
    </nav>
//--------------------------------------------------------------------------------------------------------------------------------
const CategoryListItem = ({_id, name}) =>
    <Link to={`/category/:${_id}`}>
        <li className='CatLink' style={{color:'dodgerblue'}}>{name}</li>
    </Link>

const CategoryList = ({cats}) =>
    <ul>{cats.map((item) => <CategoryListItem {...item}/>)}</ul>

const CCategoryList = connect(state => ({cats:state.promise.rootCats?.payload || []}))(CategoryList)

const Aside = () =>
    <aside><CCategoryList /></aside>
//--------------------------------------------------------------------------------------------------------------------------------
const GoodCard = ({good:{_id, name, price, images}, onAdd}) =>
    <li className='GoodCard'> 
        <h2>{name}</h2>
        <Link to={`/good/:${_id}`}>На страницу товара</Link>
        <br/>
        {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
        <br/>
        <strong>Цена: {price}</strong>
        <br/>
        <button onClick={() => onAdd({_id, name, price, images})}>Добавить в корзину</button>
    </li>

const CGoodCard = connect(null, {onAdd: actionCartAdd})(GoodCard)
//--------------------------------------------------------------------------------------------------------------------------------
const GoodPg = ({good:{_id, name, price, images, description}, onAdd}) =>
    <div className='GoodCard'> 
        <h2>{name}</h2>
        <br/>
        {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
        <br/>
        <strong>Цена: {price}</strong>
        <br/>
        <p>{description}</p>
        <br />
        <button onClick={() => onAdd({_id, name, price, images})}>Добавить в корзину</button>
    </div>

const CGoodPg = connect(state => ({good:state.promise.goodById?.payload || {}}),{onAdd: actionCartAdd})(GoodPg)

const PageGood = ({match: {params: {_id}}, getData}) => {
    useEffect(() => {
        getData(_id.substring(1))
        console.log('get', _id,typeof _id)
    },[_id])
    return (
        <CGoodPg />
    )

}

const CPageGood = connect(null, {getData: actionGoodById})(PageGood)
//--------------------------------------------------------------------------------------------------------------------------------
const LogBtn = () =>
    <Link to="/login" style={{color:'white', textDecoration:'none'}}>
        <div className='KoshikCnt item'>Войти / Выйти</div>
    </Link>

const RegBtn = () =>
    <Link to="/registration" style={{color:'white', textDecoration:'none'}}>
        <div className='KoshikCnt item'>Зарегистрироваться</div>
    </Link>


const Koshik = ({cart}) => {
    let goodsInCart = cart
    let allGoodsInCart = 0
    for (let key in goodsInCart) {
        allGoodsInCart += goodsInCart[key].count
    }
    return (
        <Link to="/cart" style={{color:'white', textDecoration:'none'}}>
            <div className='KoshikCnt item'>
                Корзина: {allGoodsInCart}
            </div>
        </Link>
    )
}

const CKoshik = connect(({cart}) => ({cart}))(Koshik)
//--------------------------------------------------------------------------------------------------------------------------------
const Category = ({cat:{name, goods=[]}={}}) => 
    <div className='Category'>
        <h1>{name}</h1>
        <ul>
            {(goods || []).map(good => <CGoodCard good={good} />)}
        </ul>
    </div>

const CCategory = connect(state => ({cat:state.promise.catById?.payload || {}}))(Category)

const PageCategory = ({match: {params: {_id}}, getData}) => {
    useEffect(() => {
        getData(_id.substring(1))
        console.log('get', _id,typeof _id)
    },[_id])
    return ( <CCategory /> )
}

const CPageCategory = connect(null, {getData: actionCatById})(PageCategory)
//--------------------------------------------------------------------------------------------------------------------------------
const CartItem = ({cart:{_id, name, price, images}, count: {count}, onChange, onRemove}) => {
    console.log('good', _id)
    return(
        <li className='GoodCard'> 
            <h2>{name}</h2>
            <Link to={`/good/:${_id}`}>На страницу товара</Link>
            <br/>
            {images && images[0] && images[0].url && <img className='GoodImg' alt='img' src={backendURL + '/' + images[0].url} />}
            <br/>
            <strong>Цена: {price * count}</strong>
            <br/>
            <label>Кол-во покупки: <input type="number" value={count} min="1" onInput={(e) => onChange({_id, name, price, images}, e.target.value)}/></label>
            <br/>
            <button disabled={!localStorage.authToken}>{localStorage.authToken? 'Заказать' : 'Авторизуйтесть чтобы заказать'}</button>
            <button onClick={() => onRemove({_id, name, price, images})}>Удалить заказ[X]</button>
        </li>
    )
}

const CCartItem = connect(null, {onChange: actionCartChange, onRemove: actionCartRemove})(CartItem)

const Cart = ({cart}) => {
    let cartArr = []
    for(let item in cart) {
        cartArr.push(cart[item])
    }
    return(
        <div>
            <h1 style={{marginLeft:'30px'}}>Корзина</h1>
            <ul>{cartArr.map(item => <CCartItem cart={item.good} count={item} />)}</ul>
        </div>
    )
}

const CCart = connect(state => ({cart:state.cart}))(Cart)
//--------------------------------------------------------------------------------------------------------------------------------
const LoginForm = ({log:{sub}, onLogin, onLogout}) => {
    let [pass, setPass] = useState()
    let [login, setLogin] = useState()
    return (
        <div className='form' style={{margin: '0 auto'}}>
            <h3>Войти</h3>
            <input placeholder='Логин' style={{outlineColor: login? 'black' : 'firebrick'}} onChange={(e) => setLogin(e.target.value)}/>
            <br/>
            <input placeholder='Пароль' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
            <br/>
            <button disabled={!pass || !login || localStorage.authToken} onClick={() => onLogin(login, pass)}>{localStorage.authToken? 'Авторизация была выполнена.' : 'Войти'}</button>
            {localStorage.authToken && <button onClick={() => onLogout()}>Выйти</button>}
            <br/>
            {sub && <small>Пользователь {sub.login} авторизован</small>}
        </div>
    )
}

const CLoginForm = connect(state => ({log: state.auth?.payload || {}}), {onLogin: actionFullLogin, onLogout: actionAuthLogout})(LoginForm)
//--------------------------------------------------------------------------------------------------------------------------------
const RegForm = ({reg:{login}, loged: {sub}, onRegister, onFullRegister}) => {
    let [pass, setPass] = useState()
    let [log, setLog] = useState()
    return (
        <div className='form' style={{margin: '0 auto'}}>
            <h3>Зарегистрироваться</h3>
            <input placeholder='Логин' style={{outlineColor: log? 'black' : 'firebrick'}} onChange={(e) => setLog(e.target.value)}/>
            <br/>
            <input placeholder='Пароль' type="password" style={{outlineColor: pass? 'black' : 'firebrick'}} onChange={(e) => setPass(e.target.value)}/>
            <br/>
            <button disabled={!pass || !log || localStorage.authToken} onClick={() => onRegister(log, pass)}>{localStorage.authToken? 'Авторизация была выполнена' : 'Зарегистрироваться'}</button>
            <br/>
            {!localStorage.authToken && <button disabled={!pass || !log} onClick={() => {onFullRegister(log, pass); }}>Зарегистрироваться и Войти</button>}
            <br/>
            {login && `Пользователь ${login} зарегистрирован ${sub && 'и авторизован' || ''}!`}
        </div>
    )
}

const CRegForm = connect(state => ({reg: state.promise.registration?.payload || {}, loged: state.auth?.payload || {}}), {onRegister: actionRegister, onFullRegister: actionFullRegister})(RegForm)
//--------------------------------------------------------------------------------------------------------------------------------


const PageMain = () => <h1>MAIN PAGE</h1>

const Page404 = () => <h1> 404 </h1>

const Main = () =>
    <main>
        <Aside />
        <Content style={{border:'1px solid black'}}>
            <Switch>
                <Redirect from='/main' to='/' />
                <Route path="/" component={PageMain} exact/>
                <Route path="/category/:_id" component={CPageCategory}/>
                <Route path="/good/:_id" component={CPageGood}/>
                <Route path="/cart" component={CCart} />
                <Route path="/login" component={CLoginForm} />
                <Route path="/registration" component={CRegForm} />
                <Route path="*" component={Page404} />
            </Switch>
        </Content>
    </main>

const Content = ({children}) =>
    <div className="Content">{children}</div>


const Footer = () =>
    <footer><Logo /></footer>


const history = createHistory()


function App() {
    return (
        <Router history={history}>
            <Provider store={store}>
                <div className="App">
                    <Header />
                    <Navbar />
                    <Main />
                    <Footer />
                </div>
            </Provider>
        </Router>
    );
}

export default App;
