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

const history = createHistory()
const jwtDecode = token => {
    try {
        let arrToken = token.split('.')
        let base64Token = atob(arrToken[1])
        return JSON.parse(base64Token)
    }
    catch (e) {
        console.log('Лажа, Бро ' + e);
    }
}
function authReducer(state, { type, token }) {
    if (!state) {
        if (localStorage.authToken) {
            type = 'AUTH_LOGIN'
            token = localStorage.authToken
        } else state = {}
    }
    if (type === 'AUTH_LOGIN') {
        localStorage.setItem('authToken', token)
        let payload = jwtDecode(token)
        if (typeof payload === 'object') {
            return {
                ...state,
                token,
                payload
            }
        } else return state
    }
    if (type === 'AUTH_LOGOUT') {
        localStorage.removeItem('authToken')
        return {}
    }
    return state
}
const actionAuthLogin = token => ({ type: 'AUTH_LOGIN', token })
const actionAuthLogout = () => ({ type: 'AUTH_LOGOUT' })

function cartReducer(state = {}, { type, good = {}, count = 1 }) {
    const { _id } = good
    const types = {
        CART_ADD() {
            count = +count
            if (!count) return state
            return {
                ...state,
                [_id]: {
                    good,
                    count: count + (state[_id]?.count || 0)
                }
            }
        },
        CART_CHANGE() {
            count = +count;
            if (!count){
                return state
            }
            return {
                ...state,
                [_id]: {good, count}
            }
        },
        CART_REMOVE() {
            let { [_id]: remove, ...newState } = state
            return {
                ...newState
            }
        },
        CART_CLEAR() {
            return {}
        },
    }
    if (type in types) {
        return types[type]()
    }
    return state
}
const actionCartAdd = (good, count=1) => ({type: "CART_ADD", good, count});
const actionCardChange = (good, count) => ({type: 'CART_CHANGE', good, count})
const actionCardRemove = (good) => ({type: 'CART_REMOVE', good})
const actionCardClear = () => ({type: 'CART_CLEAR'})

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))
        }
    }
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){
                subCategories{name, _id}
                _id name goods {
                    _id name price images {
                        url
                    }
                }
            }
        }`, { 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 actionFullLogin = (login, password) =>
    async function i(dispatch) {
        let token = await dispatch(actionLogin(login, password));
        if (token) {
            dispatch(actionAuthLogin(token));
        }
    }
const actionLogin = (login, password) => {
    return actionPromise(
        "login", gql(
            `query log($l:String, $p:String) {
              login(login:$l, password:$p)
              }`,
            { l: login, p: password }
        )
    );
}
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))
            }
        }
    }

const getGQL = url =>
    async (query, variables = {}) => {
        let obj = await fetch(url, {
            method: 'POST',
            headers: {
                "Content-Type": "application/json",
                Authorization: localStorage.authToken ? 'Bearer ' + localStorage.authToken : {},
            },
            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 store = createStore(combineReducers({promise: promiseReducer,
    auth: authReducer,
    cart: cartReducer}), applyMiddleware(thunk))
store.subscribe(() => console.log(store.getState()))
store.dispatch(actionRootCats())


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

const Koshik = ({cart}) => {
    let count = 0;
    let sum = Object.entries(cart).map(([, val]) => val.count)
    count = sum.reduce((a, b) => a + b, 0)
    return (
        <div>
            <Link className='Koshik' to={`/order`}>Корзина: {count}</Link>
        </div>
    )
}
const CKoshik = connect(({cart}) => ({cart}))(Koshik)

const Header = ({logo=logoDefault}) =>
    <header>
      <Logo logo={logo} />
      <CKoshik />
      <CPageAuthorizations/>
    </header>

const Footer = ({logo=logoDefault}) =>
    <footer>
      <Logo logo={logo} />
    </footer>

const defaultRootCats = [
  {
    "_id": "5dc49f4d5df9d670df48cc64",
    "name": "Airconditions"
  },
  {
    "_id": "5dc458985df9d670df48cc47",
    "name": "     Smartphones"
  },
  {
    "_id": "5dc4b2553f23b553bf354101",
    "name": "Крупная бытовая техника"
  },
  {
    "_id": "5dcac1b56d09c45440d14cf8",
    "name": "Макароны"
  }]

const RootCategory = ({cat:{_id, name}={}}) =>
    <li>
      <Link to={`/category/${_id}`}>{name}</Link>
    </li>

const RootCategories = ({cats=defaultRootCats}) =>
    <ul className='RootCategories'>
      {cats.map(cat => <RootCategory cat={cat} />)}
    </ul>

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

const Aside = () =>
    <aside>
      <CRootCategories />
    </aside>

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

const defaultCat ={
  "subCategories": null,
  "_id": "5dc458985df9d670df48cc47",
  "name": "     Smartphones",
  "goods": [
    {
      "_id": "61b105f9c750c12ba6ba4524",
      "name": "iPhone ",
      "price": 1200,
      "images": [
        {
          "url": "images/50842a3af34bfa28be037aa644910d07"
        }
      ]
    },
    {
      "_id": "61b1069ac750c12ba6ba4526",
      "name": "iPhone ",
      "price": 1000,
      "images": [
        {
          "url": "images/d12b07d983dac81ccad404582a54d8be"
        }
      ]
    },
    {
      "_id": "61b23f94c750c12ba6ba472a",
      "name": "name1",
      "price": 1214,
      "images": [
        {
          "url": null
        }
      ]
    },
    {
      "_id": "61b23fbac750c12ba6ba472c",
      "name": "smart",
      "price": 1222,
      "images": [
        {
          "url": "images/871f4e6edbf86c35f70b72dcdebcd8b2"
        }
      ]
    }
  ]
}

const SubCategories = ({cats}) => <></>

const GoodCard = ({good:{_id, name, price, images}={}, onCartAdd}) =>
    <div className='GoodCard'>
      <h2>{name}</h2>
      {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
      <p>Цена: {price}</p>
      <Link to={`/good/${_id}`}>Страница товара</Link><br/>
      <button onClick={() => onCartAdd({_id, name, price, images})}>+</button>
    </div>

const CGoodCard = connect(null, {onCartAdd: actionCartAdd})(GoodCard)

const Category = ({cat:{_id, name, goods, subCategories}=defaultCat}) =>
    <div className='Category'>
      <h1>{name}</h1>
      {subCategories && <SubCategories cats={subCategories} />}
      {(goods || []).map(good => <CGoodCard good={good}/>)}
    </div>

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

//корзина
const actionOrder = (card) =>
    async (dispatch, state) => {
        let orderGoods = Object.entries(card).map(([_id, {good ,count}]) => ({good: {_id}, count}))
        let res = await dispatch(actionPromise('order', gql(`
                    mutation order($order:OrderInput){
                      OrderUpsert(order:$order)
                        { _id total }
                    }
            `, {order: {orderGoods}})))
    }
const CartItem = ({item, onCartChange, onCartRemove}) => {
    const {good, count} = item[1]
    let [value, setValue] = useState(count)
    return (
        <tr className='CartItem'>
            <td>{good.name}</td>
            <td><img src={backURL + '/' + good.images[0].url}/></td>
            <td>{good.price}$</td>
            <td><input type='number' min='0' max='100' value={value} onChange={e => {setValue(e.target.value.toString()); onCartChange(good, e.target.value)}}/></td>
            <td><button type='number' onClick={() => onCartRemove(good)}>Remove</button></td>
        </tr>
    )
}
const CCartItem = connect(null, {onCartChange: actionCardChange, onCartRemove: actionCardRemove})(CartItem)

const FullCart = ({cart = {}, onCartClear, onActionOrder}) => {
    let arrCart = Object.entries(cart);
    if (arrCart.length !== 0){
        return (
            <table>
                <tr><th>Название товара</th><th>Изображение</th><th>Цена</th><th>Количество</th><th>Убрать</th></tr>
                {(arrCart || []).map(item => <CCartItem item={item}/>)}
                <tr>
                    <button onClick={() => onCartClear()}>Очистить корзину</button>
                    <button disabled={!localStorage.authToken} onClick={() => onActionOrder(cart)}>Заказать</button>
                    {!localStorage.authToken && <span>Авторизируйтесь!!!!</span>}
                </tr>
            </table>
        )
    }
    else {
        return (
            <div>Корзина пуста</div>
        )
    }
}
const CFullCart = connect(state => ({cart: state.cart}), {onCartClear: actionCardClear, onActionOrder: actionOrder})(FullCart)

const Order = ({state}) => {
    if (state?.promise?.order?.payload) {
        console.log(state.promise.order.payload)
        return(
            <div>
                <p>Ваш заказ успешно отработан!!!</p>
                <p>ID заказа: {state.promise.order.payload['_id']}</p>
                <p>На сумму: {state.promise.order.payload['total']}</p>
            </div>
        )
    }
    else {
        return (
            <CFullCart/>
        )
    }
}
const COrder = connect(state => ({state: state}))(Order)

// логин - регистрация
const LoginForm = ({onLogin, onRegister}) => {
    let [login, setLogin] = useState('')
    let [password, setPassword] = useState('')
    return(
        <div className='LoginForm'>
            <input style={{border: (login === '') ? '1px solid red': '1px solid black'}} value={login} onChange={e => setLogin(e.target.value.toString())} type='text'/>
            <input style={{border: (password === '') ? '1px solid red': '1px solid black'}} value={password} onChange={e => setPassword(e.target.value.toString())} type='password'/>
            <button disabled={login === '' && password === '' || login === '' && password !== ''|| login !== '' && password === ''}
                    onClick={() => onLogin(login, password)}>Login</button>
            <button disabled={login === '' && password === '' || login === '' && password !== ''|| login !== '' && password === ''}
                    onClick={() => onRegister(login, password)}>Registration</button>
        </div>
    )
}
const CLoginForm = connect(null,{onLogin: actionFullLogin, onRegister: actionFullRegister})(LoginForm)

const AuthAccess = ({auth}) => {
    if (auth.payload){
        let {iat} = auth.payload
        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);
        return (
            <div>
                <h1>Страница авторизации</h1>
                <p>Вы успешно авторизированны!!!</p>
                <p>Ваш логин:{auth.payload.sub.login}</p>
                <p>Ваш ID: {auth.payload.sub.id}</p>
                <p>Время авторизации: {formattedTime}</p>
            </div>
        )
    }
    else {
        return (
            <CLoginForm/>
        )
    }
}
const CAuthAccess = connect(state => ({auth: state.auth}))(AuthAccess)

//Страницы
const PageMain = () =>
    <h1>Главная страница</h1>
const PageCategory = ({match:{params:{_id}}, getData, history}) => {
  useEffect(() => {
    getData(_id)
  },[_id])
  return(
      <CCategory />
  )
}
const CPageCategory = connect(null, {getData: actionCatById})(PageCategory)

const PageAuthorizations = ({auth, actionLogOut}) => {
    if (auth.payload){
        return (
            <div>
                <strong>Вы успешно авторизованы! Привет {auth.payload.sub.login}</strong>
                <button onClick={() => actionLogOut()}>Выйти</button>
            </div>
        )
    }
    else {
        return (
            <Link to={`/authorizations`}>Войти/Зарегистрироваться</Link>
        )
    }
}
const CPageAuthorizations = connect(state => ({auth: state.auth}),{actionLogOut: actionAuthLogout})(PageAuthorizations)

const Good = ({good:{_id, name, description, price, images}={}, onCartAdd}) => {
    return (
        <div className='good'>
            <h1>Страница товара</h1>
            <h1>{name}</h1>
            {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
            <p><strong>Описание:</strong> {description}</p>
            <p><strong>ID:</strong> {_id}</p>
            <strong>Цена: {price} USD</strong>
            <button onClick={() => onCartAdd({_id, name, price, images})}>Добавить в корзину</button>
        </div>
    )
}
const CGood = connect(state => ({good: state.promise.goodById?.payload}),{onCartAdd: actionCartAdd})(Good)

const PageGood = ({match:{params:{_id}}, getData}) => {
    useEffect(() => {
        getData(_id)
    },[_id])
    return (
        <CGood/>
    )
}
const CPageGood = connect(null, {getData: actionGoodById})(PageGood)
const Page404 = () => <h1>PAGE НЭМА</h1>

const Main = () =>
    <main>
      <Aside />
      <Content>
        <Switch>
          <Route path="/" component={PageMain} exact />
          <Route path="/category/:_id" component={CPageCategory} />
          <Route path="/authorizations" component={CAuthAccess} />
          <Route path="/order" component={COrder} />
          <Route path="/good/:_id" component={CPageGood} />
          <Route path="*" component={Page404} />
        </Switch>
      </Content>
    </main>



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

export default App;
