import React, { useEffect, useReducer, useState } from 'react';
import axios from 'axios';
import qs from 'qs'
import { camelCase, get, omit, upperFirst } from 'lodash';
import { Redirect, useRouteMatch, useHistory, } from 'react-router-dom';
import { BaselineAlignment, auth, useQuery, LoadingIndicator, LoadingIndicatorPage } from 'strapi-helper-plugin';
import { Padded } from '@buffetjs/core';
import PropTypes from 'prop-types';
import forms from 'ee_else_ce/containers/AuthPage/utils/forms';
import './style.css'
import {SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_EXCG_TOKEN, SLACK_USERDETAIL_URI, STRAPI_CUSTOM_REGISTER_URI, STRAPI_AUTHOR_URI, STRAPI_USER_EXISTS_URI} from '../../config'

import NavTopRightWrapper from '../../components/NavTopRightWrapper';
import PageTitle from '../../components/PageTitle';
import LocaleToggle from '../LocaleToggle';
import checkFormValidity from '../../utils/checkFormValidity';
import formatAPIErrors from '../../utils/formatAPIErrors';
import init from './init';
import { initialState, reducer } from './reducer';

const AuthPage = ({ hasAdmin, setHasAdmin }) => {
  const { push } = useHistory();
  let showModal = 'fake-class'
  const {
    params: { authType },
  } = useRouteMatch('/auth/:authType');

  let username = '', password = '';
  let reRender = false


  const query = useQuery();
  const registrationToken = query.get('registrationToken'); 
  const { Component, endPoint, fieldsToDisable, fieldsToOmit, inputsPrefix, schema, ...rest } = get(
    forms,
    authType,
    {}
  );
  const [{ formErrors, modifiedData, requestError }, dispatch] = useReducer(
    reducer,
    initialState,
    init
  );
  const CancelToken = axios.CancelToken;
  const source = CancelToken.source();

  useEffect(() => {
    // Cancel request on unmount
    return () => {
      source.cancel('Component unmounted');
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Reset the state on navigation change
  useEffect(() => {
    dispatch({
      type: 'RESET_PROPS',
    });
  }, [authType]);

  useEffect(() => {
    if (authType === 'register') {
      const getData = async () => {
        try {
          const {
            data: { data },
          } = await axios.get(
            `${strapi.backendURL}/admin/registration-info?registrationToken=${registrationToken}`
          );

          if (data) {
            dispatch({
              type: 'SET_DATA',
              data: { registrationToken, userInfo: data },
            });
          }
        } catch (err) {
          const errorMessage = get(err, ['response', 'data', 'message'], 'An error occurred');

          strapi.notification.toggle({
            type: 'warning',
            message: errorMessage,
          });

          // Redirect to the oops page in case of an invalid token
          // @alexandrebodin @JAB I am not sure it is the wanted behavior
          push(`/auth/oops?info=${encodeURIComponent(errorMessage)}`);
        }
      };

      getData();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [authType]);

  const handleChange = ({ target: { name, value } }) => {
    dispatch({
      type: 'ON_CHANGE',
      keys: name,
      value,
    });
  };

  const handleSubmit = async e => {
    e.preventDefault();

    dispatch({
      type: 'SET_ERRORS',
      errors: {},
    });

    const errors = await checkFormValidity(modifiedData, schema);

    dispatch({
      type: 'SET_ERRORS',
      errors: errors || {},
    });

    if (!errors) {
      const body = omit(modifiedData, fieldsToOmit);
      const requestURL = `/admin/${endPoint}`;

      if (authType === 'login') {
        await loginRequest(body, requestURL);
      }

      if (authType === 'register' || authType === 'register-admin') {
        await registerRequest(body, requestURL);
      }

      if (authType === 'forgot-password') {
        await forgotPasswordRequest(body, requestURL);
      }

      if (authType === 'reset-password') {
        await resetPasswordRequest(body, requestURL);
      }
    }
  };

  const forgotPasswordRequest = async (body, requestURL) => {
    try {
      await axios({
        method: 'POST',
        url: `${strapi.backendURL}${requestURL}`,
        data: body,
        cancelToken: source.token,
      });

      push('/auth/forgot-password-success');
    } catch (err) {
      console.error(err);

      strapi.notification.toggle({
        type: 'warning',
        message: { id: 'notification.error' },
      });
    }
  };

  const loginRequest = async (body, requestURL) => {
    try {
      const {
        data: {
          data: { token, user },
        },
      } = await axios({
        method: 'POST',
        url: `${strapi.backendURL}${requestURL}`,
        data: body,
        cancelToken: source.token,
      });

      auth.setToken(token, true); //modifiedData.rememberMe
      auth.setUserInfo(user, true); //modifiedData.rememberMe

      push('/');
    } catch (err) {
      if (err.response) {
        const errorMessage = get(err, ['response', 'data', 'message'], 'Something went wrong');
        const errorStatus = get(err, ['response', 'data', 'statusCode'], 400);

        if (camelCase(errorMessage).toLowerCase() === 'usernotactive') {
          push('/auth/oops');

          dispatch({
            type: 'RESET_PROPS',
          });

          return;
        }

        dispatch({
          type: 'SET_REQUEST_ERROR',
          errorMessage,
          errorStatus,
        });
      }
    }
  };

  const registerRequest = async (body, requestURL) => {
    try {
      const {
        data: {
          data: { token, user },
        },
      } = await axios({
        method: 'POST',
        url: `${strapi.backendURL}${requestURL}`,
        data: body,
        cancelToken: source.token,
      });

      auth.setToken(token, true);
      auth.setUserInfo(user, true);

      if (
        (authType === 'register' && modifiedData.userInfo.news === true) ||
        (authType === 'register-admin' && modifiedData.news === true)
      ) {
        axios({
          method: 'POST',
          url: 'https://analytics.strapi.io/register',
          data: {
            email: user.email,
            username: user.firstname,
          },
        });
      }
      // Redirect to the homePage
      setHasAdmin(true);
      push('/');
    } catch (err) {
      if (err.response) {
        const { data } = err.response;
        const apiErrors = formatAPIErrors(data);

        dispatch({
          type: 'SET_ERRORS',
          errors: apiErrors,
        });
      }
    }
  };

  const resetPasswordRequest = async (body, requestURL) => {
    try {
      const {
        data: {
          data: { token, user },
        },
      } = await axios({
        method: 'POST',
        url: `${strapi.backendURL}${requestURL}`,
        data: { ...body, resetPasswordToken: query.get('code') },
        cancelToken: source.token,
      });

      auth.setToken(token, false);
      auth.setUserInfo(user, false);

      // Redirect to the homePage
      push('/');
    } catch (err) {
      if (err.response) {
        const errorMessage = get(err, ['response', 'data', 'message'], 'Something went wrong');
        const errorStatus = get(err, ['response', 'data', 'statusCode'], 400);

        dispatch({
          type: 'SET_REQUEST_ERROR',
          errorMessage,
          errorStatus,
        });
      }
    }
  };

  // const customLogin = async (body, route) => {
  //   await loginRequest( body, route);
  // }

  const exchangeToken = async (code) => {
    let data = qs.stringify({
      'code': code,
     'client_id': SLACK_CLIENT_ID,
     'client_secret': SLACK_CLIENT_SECRET 
     });
     let config = {
       method: 'post',
       url: SLACK_EXCG_TOKEN,
       headers: { 
         'Content-Type': 'application/x-www-form-urlencoded'
       },
       data : data
     };

     let res = await axios(config)
     if(res.data.ok) {
      // console.log("res is", res);
      // console.log("access_token", res.data.authed_user.access_token);
      return res.data.authed_user.access_token;
     }
     else {
      return null;
     } 
  }

  const userDetails = async (bearerToken) => {
    let config = {
      method: 'post',
      url: SLACK_USERDETAIL_URI,  
      headers: { 
        'Authorization': `Bearer ${bearerToken}`
      }
    };
    let res = await axios(config);
    // console.log("resssss is ", res)
    console.log("username", res.data.data.user.name)
    if(res.data.data.ok) {
      return { firstname : res.data.data.user.name, email: res.data.data.user.email};
    }
    else {
      return false;
    } 

  }

  const userExists = async (email) => {
  let data = {email: email}
  let config = {
      method: 'post',
      url: STRAPI_USER_EXISTS_URI,
      headers: { 'Content-Type': 'application/json' },
      data: data
    };
    
    let res = await axios(config);
    // console.log("new res ", res)
    return res.data.status;
  }
  const fetchUserDetails = async (code) => {
        let bearerToken =  await exchangeToken(code)
        if(bearerToken) {
          let user = await userDetails(bearerToken);
          if(user){
            let firstname = user.firstname;
            let lastname = "Doe";
            let email = user.email;
            let isUserExists = await userExists(email);
            if(isUserExists) {
              await loginRequest({email: email, password: "Strapi@123"}, "/admin/login");
              return;
            }
            let registrationToken = await registerUser({ firstname, lastname, email})
             let body = {
                "registrationToken": registrationToken,
                "userInfo":{"firstname":firstname,"lastname":lastname,"password":"Strapi@123"}
            }
            if(registrationToken) {
                showModal = 'outer-modal'
                await registerRequest(body, '/admin/register');
                localStorage.setItem("mode", "slack")
            }
            return;
          }
        }

        else return false;
  }

  const fetchAuthorRole = async () => {
    let config = {
      method: 'get',
      url: STRAPI_AUTHOR_URI,
      headers: { }
    };
    let res = await axios(config);
    // console.log(res.data)
    for(let role of res.data.data) {
      if(role.code=='strapi-author') return role.id;
    }
    alert("Unable to fetch user role.");
    return false;
  }

  const registerUser = async (userDetails) => {
    try {
      const role = await fetchAuthorRole();
      if(role) {
        userDetails.roles = [role]
        let data = JSON.stringify(userDetails)
        let  config = {
          method: 'post',
          url: STRAPI_CUSTOM_REGISTER_URI,  
          headers: { 
            'Content-Type': 'application/json'
          },
          data : data
        };
        let details =await axios(config);
        // console.log(details)
        if(details.statusCode==400) {
            //got the registration token and use tht to login user... do the rest later
            //show alert
            // console.log("user already exists", STRAPI_CUSTOM_REGISTER_URI)
            alert('user already exist. Please use another slack account to login.')
            return false;
        }
        else if( details.data && details.data.data && details.data.data.registrationToken) {
            return details.data.data.registrationToken;
        }
        else {
          alert('User already registered.')
          console.log('Something went wrong.....Please try again later.')
          return false
        }
  
      }
    }
    catch(err) {
      console.log(err)
        alert('Unable to register user.')
        return false
    }

  }

  const code = query.get('code')
  if(code) {
    showModal = 'outer-modal';
    fetchUserDetails(code);
  }
  if (!forms[authType] || (hasAdmin && authType === 'register-admin') || auth.getToken()) {
    return <Redirect to="/" />;
  }

  // Redirect the user to the register-admin if it is the first user
  if (!hasAdmin && authType !== 'register-admin') {
    return <Redirect to="/auth/register-admin" />;
  }
  let hide = '', loader = 'hide-loader';
  if(showModal=='outer-modal') {
    hide = 'hide-div';
    loader = 'show-loader';
  }

  return (
    <div >
      <div className = {loader}>
      <LoadingIndicatorPage>
        <LoadingIndicator />
      </LoadingIndicatorPage>
      </div>

      <div className = {hide}>
        <Padded bottom size="md">
        <PageTitle title={upperFirst(authType)} />
        <NavTopRightWrapper>
          <LocaleToggle isLogged className="localeDropdownMenuNotLogged" />
        </NavTopRightWrapper>
        <BaselineAlignment top size="78px">
          <Component
            {...rest}
            fieldsToDisable={fieldsToDisable}
            formErrors={formErrors}
            inputsPrefix={inputsPrefix}
            modifiedData={modifiedData}
            onChange={handleChange}
            onSubmit={handleSubmit}
            username = {username}
            password = {password}
            requestError={requestError}
            reRender = { reRender }
          />
        </BaselineAlignment>
        </Padded>
      </div>
    </div>
  );
};

AuthPage.defaultProps = {
  hasAdmin: false,
};

AuthPage.propTypes = {
  hasAdmin: PropTypes.bool,
  setHasAdmin: PropTypes.func.isRequired,
};

export default AuthPage;
