import React, {useState, useEffect, useRef, Component, createRef} from 'react';
import logoDefault from './logo.svg';
import cartImg from './cart.svg';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.scss';
import {Provider, connect}   from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import {all, takeEvery, put, call, take, select} from 'redux-saga/effects';


import {Router, Route, Link, Redirect, Switch} from 'react-router-dom';
import createHistory from "history/createBrowserHistory";


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

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"
              }
          ]
      }
  ]
} 



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

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

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




function cartReducer (state={}, {type, good={}, count=1}) {

  if (Object.keys(state).length === 0 && localStorage.cart) {
      let currCart = JSON.parse(localStorage.cart)
      if (currCart && Object.keys(currCart).length !== 0) {
          state = currCart
      } 
  }

  const {_id} = good

  const types = {
      CART_ADD() {
          count = +count
          if (!count) {
              return state
          }
          let newState = {
              ...state,
              [_id]: {good, count: (count + (state[_id]?.count || 0)) < 1 ? 1 : count + (state[_id]?.count || 0)}
          }
          localStorage.cart = JSON.stringify(newState) 
          return newState
      },
      CART_CHANGE() {
          count = +count
          let newState = null
          // if (!count) {
          //   return state
          // } 
          newState = {
            ...state,
            [_id]: {good, count: count < 1 ? 1 : count}
          }
          localStorage.cart = JSON.stringify(newState) 
          return newState
      },
      CART_REMOVE() {           
          let { [_id]: removed, ...newState }  = state
          localStorage.cart = JSON.stringify(newState) 
          return newState
      },
      CART_CLEAR() {
          localStorage.cart = JSON.stringify({}) 
          return {}
      },
  }
  if (type in types) {        
      return types[type]()
  }
  return state
}

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




function promiseReducer(state={}, {type, status, payload, error, name}) {
  if (!state) {
      return {}
  }
  if (type === 'PROMISE') {         
      return {
          ...state,
          [name]: {
              status: status,
              payload : payload,
              error: 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) => (
  {type: 'PROMISE_START', 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 getGQL = url => (
  async (query, variables={}) => {
      let obj = await fetch(url, {
        method: 'POST',
        headers: {
          "Content-Type": "application/json",
          ...(localStorage.authToken ? {Authorization: "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))
      } else {
          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 actionOrder = () => (
    async (dispatch, getState) => {
        let {cart} = getState()
        const orderGoods = Object.entries(cart)
                            .map(([_id, {good, count}]) => ({good: {_id}, count}))
  
        let result = await dispatch(actionPromise('order', gql(`
                    mutation newOrder($order:OrderInput){
                      OrderUpsert(order:$order)
                        { _id total}
                    }
            `, {order: {orderGoods}})))
            if (result?._id) {
                dispatch(actionCartClear())
            }
    }
  )
    
    
  
  
  const actionLogin = (login, password) => (
      actionPromise('login', gql(`query log($login: String, $password: String) {
          login(login: $login, password: $password)
      }`, {login, password}))
  )
  
  const actionFullLogin = (login, password) => (
    {type: 'FULL_LOGIN', login, password}
      // async (dispatch) => {
      //     let token = await dispatch(actionLogin(login, password))
      //     if (token) {
      //         dispatch(actionAuthLogin(token))
      //     }
      // }
  )
    
  const actionRegister = (login, password) => (
      actionPromise('register', gql(`mutation reg($user:UserInput) {
          UserUpsert(user:$user) {
          _id 
          }
      }
      `, {user: {login, password}})      
      )
  )
  
  const actionFullRegister = (login, password) => (
      async (dispatch) => {
          let regId = await dispatch(actionRegister(login, password))
          if (regId) {
              dispatch(actionFullLogin(login, password))
          }
      }
  )
  
  
  
  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 
              }
          }
      }`, {q: JSON.stringify([{_id}])}))
  )
  
  const actionGoodById = (_id) => (
      actionPromise('goodById', gql(`query goodById($q: String) {
          GoodFindOne(query: $q) {
              _id name price description images {
              url
              }
          }
      }`, {q: JSON.stringify([{_id}])}))
  )
  
  const actionGoodsByUser = (_id) => (
      actionPromise('goodByUser', gql(`query oUser($query: String) {
          OrderFind(query:$query){
          _id orderGoods{
                  price count total good{
                      _id name categories{
                      name
                      }
                      images {
                          url
                      }
                  }
              } 
              owner {
              _id login
              }
          }
      }`, 
      {query: JSON.stringify([{___owner: _id}])}))
  )
  
  
  const actionGoodFind = (word) => (
      actionPromise('goodFind', gql(`query goodById($q: String) {
          GoodFind(query: $q) {
              _id name price description images {
              url
              }
          }
      }`, {q: JSON.stringify([
              {
                  $or: [{title: `/${word}/`}, {description: `/${word}/`}, {name: `/${word}/`}] 
              },
              {
                  sort: [{title: 1}]  
              } 
              ])
          }
      ))
  )




// const store = createStore(  combineReducers({ promise: promiseReducer, 
//                                               auth: authReducer, 
//                                               cart: cartReducer}),
//                             applyMiddleware(thunk))

const sagaMiddleware = createSagaMiddleware()   
const store = createStore(  combineReducers({ promise: promiseReducer, 
                              auth: authReducer, 
                              cart: cartReducer}),
                            applyMiddleware(sagaMiddleware))                        



//  аналог actionPromise
function* promiseWorker(action) {
  const {name, promise} = action
    yield put(actionPending(name))
    try { 
          let data = yield promise
          yield put(actionResolved(name, data))
        return data
      }
      catch(error){
          yield put(actionRejected(name, error))
      }     
}

function* promiseWatcher() {
  // запуск worker для каждого экшна типа промис старт,
  // в обработчмк приходит результат функции с промис старт
  yield takeEvery('PROMISE_START', promiseWorker)
}


function* loginWorker(action) {
  const {login, password} = action
    // аналог вложенного диспатча вложенного санка
    let token = yield call(promiseWorker, actionLogin(login, password))
    if (token) {
      yield put(actionAuthLogin(token))
    }    
}

function* loginWatcher() {
  yield takeEvery('FULL_LOGIN', loginWorker)
}

function* rootSaga() {
  yield all([
    promiseWatcher(),
    loginWatcher()
  ])
}

sagaMiddleware.run(rootSaga)







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)
    //перебрать cart, посчитать суммарный count
    return (
        <Link to='/cart' className='Koshik'>
          <img src={cartImg} />          
          {count}
        </Link>
    )
}
const CKoshik = connect(({cart}) => ({cart}))(Koshik)



const GreetContainer = ({user: {id, login}}) => {
  if (id && login) {
    return (
      <div>
        <div>
          ПРИВЕТ, {login}
        </div>
        <Link to={`/orders/${id}`} >
          Мои заказы
        </Link>
      </div>
    )
  } else {
    return (<></>)
  }
}
const CGreetContainer = connect(state => ({user: state.auth.payload?.sub || {}}))(GreetContainer)


const Header = ({logo=logoDefault}) => (
  <header className="bg-light">
    <div className="leftBlock">
      <Logo logo={logo} />
    </div>
    <div className="rightBlock">
      <CKoshik />
      <CGreetContainer />
    </div>
  </header>
)



const RootCategory = ({cat:{_id, name}={}}) => (
    <Link  to={`/category/${_id}`} 
          className="catBtn list-group-item list-group-item-action list-group-item-light">
      {name}
    </Link>
)

const RootCategories = ({cats=defaultRootCats}) => (
  <div className='RootCategories list-group linkList'>
      {cats.map(cat => <RootCategory cat={cat} />)}
  </div>
)
const CRootCategories = connect(state => ({cats: state.promise.rootCats?.payload || []}))(RootCategories)


const LogoutBTn = ({onLogout}) => (
  <button className='logoutBtn btn btn-primary'
          onClick={() => onLogout()}>
    Logout
  </button>
)
const CLogoutBTn = connect(null, {onLogout: actionAuthLogout})(LogoutBTn)


const Aside = () => (
  <aside className="bg-light">
      <div className='LoginBlock'>
        <Link to='/login' className='loginBtn btn btn-primary'>Login</Link>
        <Link to='/register' className='registerBtn btn btn-primary'>Register</Link>
        <CLogoutBTn />
      </div>
      <CRootCategories />
  </aside>
)

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



const SubCategory = ({cat:{_id, name}={}}) => ( 
  <Link to={`/category/${_id}`}
        className='list-group-item list-group-item-action list-group-item-primary linkItem'>
          {name}
  </Link>
)

const SubCategories = ({cats}) => (
  <div className="CatsList list-group linkList">
    {cats.map(cat => <SubCategory cat={cat} />)}
  </div>
)


const GoodCard = ({good:{_id, name, price, images}={}, onCartAdd}) => (
  <div className='GoodCard card'>      
      {images && images[0] && images[0].url && 
      <img className='card-img-top' src={backURL + '/' + images[0].url} />}
      <div className="card-body">
        <h4 className="card-title">{name}</h4>
        <h5>{price} грн</h5>
        <button className='btn btn-primary'
              onClick={() => onCartAdd({_id, name, price, images})}>В корзину</button>
        <Link className='btn btn-success'
              to={`/good/${_id}`}>Подробнее</Link>
      </div>
  </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} />}
      <div className='GoodCards'>
        {(goods || []).map(good => <CGoodCard good={good}/>)}
      </div>      
  </div>
)
const CCategory = connect(state => ({cat: state.promise.catById?.payload}))(Category)




const GoodInCart = ({item: {count, good: {_id, name, price, images}}, onCartChange, onCartRemove, onCartAdd}) => {
  const [goodCount, setGoodCount] = useState(count)

  return (
    <div className='CartCard card'>
        <div className="card-header">
          <h4 className="card-title">{name}</h4>
        </div>
        {images && images[0] && images[0].url && 
        <img className="card-img-top" src={backURL + '/' + images[0].url} />}
        <div className="card-body">
          <p>{count} шт</p>
          <p>{price} грн</p>   
          <h6>Итого: {count*price} грн</h6>  
          <button className="btn btn-success"
                  onClick={() => {  setGoodCount((count === 1 ) ? 1 : count - 1); 
                                    onCartAdd({_id, name, price, images}, -1) }}>-</button>
          <input  onBlur={() => onCartChange({_id, name, price, images}, goodCount)}  
                  onInput={(e) => setGoodCount(e.currentTarget.value)}
                  // onInput={(e) => onCartChange({_id, name, price, images}, e.currentTarget.value)}
                  value={goodCount} type="number"/>
          <button className="btn btn-success"
                  onClick={() => {  setGoodCount(count + 1); 
                                    onCartAdd({_id, name, price, images}) }}>+</button>
          <button className="btn btn-success delBtn"
                  onClick={() => onCartRemove({_id, name, price, images})}>Удалить</button>
        </div>
    </div>
  )
} 
const CGoodInCart = connect(null, { onCartChange: actionCartChange, 
                                    onCartRemove: actionCartRemove, 
                                    onCartAdd: actionCartAdd})(GoodInCart)


const Cart = ({cart, user, onCartClear, onOrderSend}) => {
  let total = 0
  if (cart.length !== 0) {
    let countes = Object.entries(cart).map(([, val]) => val.count)
    let prices = Object.entries(cart).map(([, val]) => val.good.price)
    let sum = countes.map((count, index) => prices[index] * count)
      total = sum.reduce((a, b) => a + b)
  }
  return (  
    <div className='Cart'>
      {cart.length === 0 ? <h5>Корзина пуста</h5> :  
      <button className='btn btn-primary'
              onClick={() => onCartClear()}>
        Очистить корзину
      </button>} 
      <div className='CartBlock'>
        {cart.map(item => <CGoodInCart item={item}/>)}  
      </div>
      {cart.length === 0 ? <></> :  
      <>
        <h5>Всего к оплате: {total} грн</h5>
        <button className='btn btn-primary'
                onClick={() => onOrderSend()}
                disabled={user ? false : true}>
          Оформить заказ
        </button>
      </>} 
    </div>
  )
}
const CCart = connect(state => ({ cart: Object.values(state.cart) || [], 
                                  user: state.auth.payload || null}), 
                                { onCartClear: actionCartClear, 
                                  onOrderSend: actionOrder})(Cart)


const LoginForm = ({onLogin}) => {
  const [login, setLogin] = useState("")
  const [pass, setPass] = useState("")

  return (
    <div className='LoginForm form'>
      <div className="mb-3">
        <label className="form-label">Логин</label>
        <input  className="form-input form-control"
                type="text"style={ {backgroundColor: (login.length === 0) ? '#f00b' : '#fff'} }
                value={login} onChange={(e) => setLogin(e.target.value)}/>
      </div>
      <div className="mb-3">
        <label className="form-label">Пароль</label>
        <input  className="form-input form-control" 
                type="password" style={ {backgroundColor: (pass.length === 0) ? '#f00b' : '#fff'} }
                value={pass} onChange={(e) => setPass(e.target.value)}/>
      </div>
      <button className="btn btn-primary"
              onClick={() => {  onLogin(login,pass);
                                setPass('')
                              }} 
              disabled={(login.length !== 0 && pass.length !== 0) ? false : true}>Войти</button>
    </div>
  )
}
const CLoginForm = connect(null, {onLogin: actionFullLogin})(LoginForm)



const RegisterForm = ({onRegister}) => {
  const [login, setLogin] = useState("")
  const [pass, setPass] = useState("")

  return (
    <div className='RegisterForm form'>
      <div className="mb-3">
        <label className="form-label">Логин</label>
        <input  className="form-input form-control" 
                type="text" style={ {backgroundColor: (login.length === 0) ? '#f00b' : '#fff'} }
                value={login} onChange={(e) => setLogin(e.target.value)}/>
      </div>
      <div className="mb-3">
        <label className="form-label">Пароль</label>
        <input  className="form-input form-control" 
                type="password" style={ {backgroundColor: (pass.length === 0) ? '#f00b' : '#fff'} }
                value={pass} onChange={(e) => setPass(e.target.value)}/>
      </div>
      <button className="btn btn-primary"
              onClick={() => {  onRegister(login,pass);
                                setPass('')
                              }} 
              disabled={(login.length !== 0 && pass.length !== 0) ? false : true}>Зарегистрироваться</button>
    </div>
  )
}
const CRegisterForm = connect(null, {onRegister: actionFullRegister})(RegisterForm)



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 ThisGood = ({good:{_id, name, price, images, description}={}, onCartAdd}) => (
  <div className='GoodPage'>
      <h2>{name}</h2>
      {images && images[0] && images[0].url && <img src={backURL + '/' + images[0].url} />}
      <div>
        <h6>{description}</h6>
        <strong>Цена - {price} грн</strong>
      </div>
      <button className="btn btn-success"
              onClick={() => onCartAdd({_id, name, price, images})}>В корзину</button>
  </div>
)
const CThisGood = connect(  state => ({good: state.promise.goodById?.payload}), 
                            {onCartAdd: actionCartAdd})(ThisGood)


const PageGood = ({match:{params:{_id}}, getData}) => {
  useEffect(() => {
      getData(_id)
  },[_id])

  return (
    <>
      <CThisGood />
    </>
  )
}
const CPageGood = connect(null, {getData: actionGoodById})(PageGood)


// на текущий момент функция отображает только 100 последних заказов
// подсчет потраченных средств нужно реализовать для всех заказов
// но при этом сразу отображать только 100 последних
const OrderedGood = ({goodOrder:{price, count, total, good}={}}) => {
  if (price !== null && count !== null && total !== null && good !== null) {
    const {_id, name, images} = good  
    return (
      <div className='OrderCard card'>
          {images && images[0] && images[0].url && 
            <img className='card-img-top' src={backURL + '/' + images[0].url} />}
          <div className="card-body">
            <h4 className="card-title">{name}</h4>
            <h6>
                Куплено: {count} по {price} грн. 
            </h6>  
            <h6>
                Итого: {total} грн
            </h6>  
            <Link className="btn btn-primary"
                  to={`/good/${_id}`}>Подробнее</Link>
          </div>
      </div>
    )
  } else {
    return <></>
  }
}

const Orders = ({orders}) => (
  <div className='Orders'>
    {/* <h3>Всего потрачено: {totalMoney} грн</h3> */}
    <div className='OrderCards'>
      { orders.map(order => 
        (order.orderGoods ? order.orderGoods: []).map(goodOrder => 
            <OrderedGood goodOrder={goodOrder}/>))}
    </div>
  </div>
)
const COrders = connect(state => ({orders: state.promise.goodByUser?.payload || []}))(Orders)


const PageOrders = ({match:{params:{_id}}, getData}) => {
  useEffect(() => {
      getData(_id)
  },[_id])

  return (
    <>
      <COrders />
    </>
  )
}
const CPageOrders = connect(null, {getData: actionGoodsByUser})(PageOrders)



const Page404 = () => (
  <h1>PAGE НЭМА </h1>
)

const Main = () => (
  <main>
      <Aside />
      <Content>
        <Redirect from="/main" to="/" />
        <Switch>
          <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="/register" component={CRegisterForm} />
          <Route path="/orders/:_id" component={CPageOrders} />
          <Route path="*" component={Page404} />
        </Switch>
      </Content>
  </main>
)


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



const history = createHistory()

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

            <Main />          

            <Footer />
          </div>
        </Provider>
      </Router>
  )
}

export default App;
