import React from 'react';
import { History } from 'react-router';
import { routeActions } from 'redux-simple-router';
import classnames from 'classnames';
import moment from 'moment';
import reactMixin from 'react-mixin';
import { connect } from 'react-redux';
import every from 'lodash/every';

// Actions
import { register, login, updateProfile, cancelUpdate } from 'actions/session';
import { attemptPurchase, billingError, makePurchase } from 'actions/billing';
import { CAN_ACCESS_BZPRO } from 'utils/session';

// Inputs
import EmailInput from '../payment/inputs/EmailInput';
import PasswordInput from '../payment/inputs/PasswordInput';
import CCNameInput from '../payment/inputs/CCNameInput';
import PhoneInput from '../payment/inputs/PhoneInput';
import CCNumberInput from '../payment/inputs/CCNumberInput';
import CCExpiryInput from '../payment/inputs/CCExpiryInput';
import CVCInput from '../payment/inputs/CVCInput';

class Checkout extends React.Component {
  constructor(props) {
    super(props);

    // If the component is going to mount with a user that's already signed in
    // set the initial state accordingly
    const { user } = props.session;
    const fname = user.get('firstname');
    const lname = user.get('lastname');
    const name = lname ? (fname + ' ' + lname) : fname;
    const phone = user.get('phone');

    this.state = {
      occurrence: props.params.occurrence || null,
      promotion: false,
      freetrial: true,
      trialDuration: 14,
      coupon: 0,
      editInfo: false,
      email: null,
      password: null,
      name: name || null,
      savedName: null,
      phone: phone || null,
      savedPhone: null,
      card: null,
      cardType: null,
      exp: null,
      cvc: null,
      validCreds: user.get('email') ? true : false,
      validInfo: user.get('firstname') && user.get('phone') ? true : false,
      validCard: false
    };

    this.prices = {
      basic: {
        monthly: 99,
        annual: 990
      },
      essential: {
        monthly: 199,
        annual: 1990
      },
      premium: {
        monthly: 299,
        annual: 2990
      }
    };
  }

  // See if we can redirect this already-logged-in user
  componentWillMount() {
    const { session } = this.props;
    this.redirectUser(session);
  }

  componentWillReceiveProps(nextProps) {
    const session = nextProps.session;
    const user = session.get('user');
    const permissions = session.get('permissions');

    // If user logs in from inside the component, make sure to redirect
    // them if they already have permissions
    this.redirectUser(session);

    // Move to the correct step and autofill fields with the provided user info
    if (user.get('email')) {
      const fname = user.get('firstname');
      const lname = user.get('lastname');
      const name = fname + ' ' + lname;
      const phone = user.get('phone');
      const { savedName, savedPhone } = this.state;
      const loading = session.get('isLoading');

      // If they have creds, move them to step 2
      if (!this.state.validCreds) {
        this.setState({validCreds: true});
      }

      // If they have user info and aren't processing an edit request, move them
      // to step 3
      if (fname && phone && !loading) {
        this.setState({
          validInfo: true,
          phone,
          name
        });
        // If there was an edit request and it has successfully updated, finish
        // the update and clear the cached values
        if (name !== savedName || phone !== savedPhone) {
          this.setState({
            editInfo: false,
            savedName: null,
            savedPhone: null
          });
        }
      }
    }
  }

  /**
   * Passed to inputs to detect a change and update state accordingly
   *
   * @param  {obj} state change
   */
  onUpdate(value) {
    this.setState(value);
  }

  /**
   * If the user has permissions, redirect them to the platform
   *
   * @param  {immutable map} session
   */
  redirectUser(session) {
    const permissions = session.get('permissions');

    if (permissions && session.hasPerm(CAN_ACCESS_BZPRO)) {
      this.props.dispatch(routeActions.replace('/dashboard/'));
    }
  }

  /**
   * Try to create a new account for the user
   */
  registerUser() {
    const { dispatch } = this.props;

    dispatch(register(this.state.email, this.state.password));
  }


  // Try to update the user's profile
  updateUserProfile() {
    const { dispatch } = this.props;
    const { name, phone, savedName, savedPhone, editInfo } = this.state;

    if (editInfo && savedName === name && savedPhone === phone) {
      this.setState({
        editInfo: false
      });

      return dispatch(cancelUpdate());
    }

    dispatch(updateProfile(name, phone));
  }

  // Store current user info in state and unlock inputs to edit
  handleEditProfile() {
    const { dispatch } = this.props;
    const { name, phone, savedName, savedPhone, editInfo } = this.state;
    if (editInfo) {
      dispatch(cancelUpdate());
      this.setState({
        editInfo: false,
        name: savedName,
        phone: savedPhone
      });
    } else {
      this.setState({
        editInfo: true,
        savedName: name,
        savedPhone: phone
      });
    }
  }


  // Detect enter key for submission
  // registerSubmit(e) {
  //   const key = e.keyCode || e.which;
  //   if (key === 13) this.attemptRegister();
  // }
  // _onEnter(e, func) {
  //   const key = e.keyCode || e.which;
  //   if (key === 13) this.attemptRegister();
  // }


  /**
   * Checks our required fields. Returns false if we're missing something.
   */
  validate() {
    const { name, card, exp, cvc } = this.state;
    return every([
      name,
      card,
      exp,
      cvc
    ], Boolean);
  }

  /**
   * Some error handling
   *
   * @param field {string} name of field to check for error
   */
  handleError(field) {
    const { billing, session } = this.props;
    const err = billing.get('error') || session.get('error');

    if (err) {
      switch (field) {
      case 'number':
      case 'cvc':
      case 'expiry':
      case 'email':
      case 'password':
      case 'name':
      case 'phone':
        return err.type === field ? err.message : null;
      case 'submit':
        return err.type === field ? (<div className="checkout-section error">{err.message}</div>) : null;
      default:
        return null;
      }
    }
  }

  /**
   * Style the given section based on it's status
   *
   * @param section {string} name of the section to apply classes to
   */
  sectionStatus(section) {
    const { validCreds, validInfo, editInfo } = this.state;
    let sectionStates = null;

    if (section === 'creds') sectionStates = { completed: validCreds };
    if (section === 'info') sectionStates = { completed: validInfo && !editInfo, disabled: !validCreds && !editInfo };
    if (section === 'billing') sectionStates = { completed: this.validate(), disabled: !validInfo };

    return classnames('checkout-section', sectionStates);
  }

  submitCheckout() {
    const { name, card, cvc, occurrence } = this.state;
    const exp = this.state.exp.replace(/ /g, '');
    const { params, dispatch } = this.props;
    const user = this.props.session.get('user');

    return dispatch(attemptPurchase(name, card, cvc, exp, params.plan, occurrence));
  }

  render() {
    const {
      promotion, coupon, validCreds, validInfo, error, occurrence, validCard,
      email, password, name, phone, card, exp, cvc, freetrial, trialDuration,
      editInfo
    } = this.state;
    const { params, session, billing, dispatch } = this.props;
    const user = session.get('user');
    const price = this.prices[params.plan][occurrence] - coupon;
    const trialEndDate = moment().add(trialDuration, 'd').format('L');

    // Loading States
    const nextLoading = session.get('isLoading');
    const submitLoading = billing.get('isLoading');

    // Button States
    const credsNext = !(email && password);
    const infoNext = !(name && phone);
    const cardNext = !(card && exp && cvc);

    // Styling
    const plansClasses = classnames('checkout-select', params.plan, {selected: params.plan});
    const occurrenceClasses = classnames('checkout-select', occurrence, {selected: occurrence});
    const registerNextClasses = classnames('checkout-btn', {'checkout-btn--loading': nextLoading && !validCreds && !credsNext});
    const updateNextClasses = classnames('checkout-btn', {'checkout-btn--loading': nextLoading && validCreds});
    const editInfoClasses = classnames('checkout-btn', {'checkout-btn--done': !validInfo});
    const submitClasses = classnames('checkout-btn checkout-btn--submit', {'checkout-btn--loading': submitLoading && this.validate()});

    // if the user object is modified during the checkout process
    // make sure we're passing them in the inputs
    const emailValue = user.get('email');
    const passwordValue = emailValue ? '********' : null;
    const firstnameValue = user.get('firstname');
    const lastnameValue = user.get('lastname');
    const fullnameValue = lastnameValue ? firstnameValue + ' ' + lastnameValue : firstnameValue;
    const phoneValue = user.get('phone');

    return (
      <div className="checkout">
        <section className="checkout-header">
          <img className="checkout-logo" src={require('../../assets/images/bzpro-logo.svg')} />
        </section>
        <div className="checkout-column">
          <section className={this.sectionStatus('creds')}>
            <h2 className="checkout-heading">Create Account</h2>
            <EmailInput disabled={validCreds} value={emailValue} onStateUpdate={(newemail) => this.onUpdate(newemail)} error={this.handleError('email')} />
            <PasswordInput disabled={validCreds} value={passwordValue} onStateUpdate={(newpassword) => this.onUpdate(newpassword)} error={this.handleError('password')} />
            <div className="checkout-field submit">
              {!validCreds ? (
                <button className={registerNextClasses} disabled={credsNext} onClick={() => this.registerUser()}>
                  <span className="checkout-btn__text">CONTINUE</span>
                  <i className="checkout-btn__spinner"></i>
                </button>
              ) : null}
            </div>
          </section>
          <section className={this.sectionStatus('info')}>
            <h2 className="checkout-heading">Basic Information</h2>
            <CCNameInput disabled={!validCreds || validInfo && !editInfo} value={name} onStateUpdate={(newname) => this.onUpdate(newname)} error={this.handleError('name')} />
            <PhoneInput disabled={!validCreds || validInfo && !editInfo} value={phone} onStateUpdate={(newphone) => this.onUpdate(newphone)} error={this.handleError('phone')} />
            <div className="checkout-field submit">
              {validInfo ? (
                <button className="checkout-btn checkout-btn--edit" onClick={() => this.handleEditProfile()}>
                  <span className="checkout-btn__text">{editInfo ? 'CANCEL' : 'EDIT'}</span>
                  <i className="checkout-btn__spinner"></i>
                </button>
              ) : null}
              {!validInfo || validInfo && editInfo ? (
                <button className={updateNextClasses} disabled={infoNext} onClick={() => this.updateUserProfile()}>
                  <span className="checkout-btn__text">{editInfo ? 'SAVE' : 'CONTINUE'}</span>
                  <i className="checkout-btn__spinner"></i>
                </button>
              ) : null}
            </div>
          </section>
          <section className={this.sectionStatus('billing')}>
            <h2 className="checkout-heading">
            Billing Information
            <svg className="checkout-secure" xmlns="http://www.w3.org/2000/svg" viewBox="33 14.3 73.9 16.1">
              <path className="checkout-secure__lock" d="M43.8 20.5V19c0-2.6-2.1-4.7-4.7-4.7s-4.7 2.1-4.7 4.7v1.5H33v9.8h12.1v-9.8h-1.3zm-3.7 5.4v1.6c0 .5-.5 1-1 1s-1-.5-1-1v-1.6c-.4-.3-.7-.8-.7-1.4 0-1 .8-1.7 1.7-1.7 1 0 1.7.8 1.7 1.7-.1.7-.4 1.2-.7 1.4zm2.1-5.4h-6.3V19c0-1.7 1.4-3.2 3.2-3.2s3.2 1.4 3.2 3.2c-.1.6-.1 1.5-.1 1.5z"/>
              <path className="checkout-secure__text" d="M52 28.8l.9-1.3c.7.7 1.7 1.3 3 1.3 1.4 0 1.9-.7 1.9-1.3 0-2-5.6-.8-5.6-4.3 0-1.6 1.4-2.8 3.5-2.8 1.5 0 2.7.5 3.6 1.3l-1 1.2c-.7-.7-1.8-1.1-2.8-1.1-1 0-1.6.5-1.6 1.2 0 1.8 5.6.7 5.6 4.2 0 1.6-1.1 2.9-3.7 2.9-1.6.2-2.9-.4-3.8-1.3zM61.6 30.2v-9.5h6.5v1.5h-4.9v2.5H68V26h-4.8v2.6h4.9v1.5h-6.5zM69.8 25.4c0-2.9 2.2-4.9 5-4.9 1.9 0 3.1 1 3.8 2.1l-1.4.7c-.5-.8-1.4-1.4-2.4-1.4-1.9 0-3.3 1.4-3.3 3.4s1.4 3.4 3.3 3.4c1 0 1.9-.6 2.4-1.4l1.4.7c-.7 1.1-1.9 2.1-3.8 2.1-2.9.2-5-1.8-5-4.7zM80.2 26.4v-5.8h1.7v5.7c0 1.5.8 2.5 2.4 2.5 1.6 0 2.4-1 2.4-2.5v-5.7h1.7v5.8c0 2.4-1.4 3.9-4.1 3.9-2.7 0-4.1-1.6-4.1-3.9zM96.4 30.2l-2.1-3.6h-1.7v3.6H91v-9.5h4.2c1.9 0 3.1 1.2 3.1 3 0 1.7-1.1 2.6-2.3 2.8l2.3 3.7h-1.9zm.2-6.6c0-.9-.7-1.5-1.6-1.5h-2.3v3H95c.9 0 1.6-.6 1.6-1.5zM100.4 30.2v-9.5h6.5v1.5H102v2.5h4.8V26H102v2.6h4.9v1.5h-6.5z"/>
            </svg>
            </h2>
            <CCNumberInput disabled={!validInfo} onStateUpdate={(newnumber) => this.onUpdate(newnumber)} error={this.handleError('number')} />
            <CCExpiryInput disabled={!validInfo} onStateUpdate={(newexpiry) => this.onUpdate(newexpiry)} error={this.handleError('expiry')} />
            <CVCInput disabled={!validInfo} onStateUpdate={(newcvc) => this.onUpdate(newcvc)} error={this.handleError('cvc')} />
          </section>
        </div>
        <div className="checkout-column">
          <section className="checkout-section">
            <h2 className="checkout-heading">Your Package</h2>
            <div className="checkout-plan">
              Benzinga Pro: {params.plan}
            </div>
            <div className={occurrenceClasses}>
              <input className="checkout-select__input"
                     id="bill-monthly"
                     type="radio"
                     name="occurrence-select"
                     value="monthly"
                     onChange={() => this.setState({occurrence: 'monthly'})}
                     checked={occurrence === 'monthly' ? true : false} />
             <label htmlFor="bill-monthly" className="checkout-select__toggle">
               <div className="checkout-select__price">{params.plan ? '$' + this.prices[params.plan].monthly : null}<small>/mo</small></div>
               <div className="checkout-select__sub">MONTHLY</div>
             </label>
             <input className="checkout-select__input"
                    id="bill-annually"
                    type="radio"
                    name="occurrence-select"
                    value="annual"
                    onChange={() => this.setState({occurrence: 'annual'})}
                    checked={occurrence === 'annual' ? true : false} />
              <label htmlFor="bill-annually" className="checkout-select__toggle">
                <div className="checkout-select__price">{params.plan ? '$' + this.prices[params.plan].annual : null}<small>/yr</small></div>
                <div className="checkout-select__sub">ANNUAL</div>
              </label>
            </div>
          </section>
          <section className="checkout-section checkout-cart">
            <div className="checkout-cart__title checkout-heading">Subscription Details</div>
            <div className="checkout-cart__row plan">
              <span className="checkout-cart__item">Benzinga Pro Plan</span>
              <span className="checkout-cart__desc">{params.plan}</span>
            </div>
            <div className="checkout-cart__row price">
              <span className="checkout-cart__item">Price After Trial</span>
              <span className="checkout-cart__desc">${price || 0}</span>
            </div>
            {promotion ? (
              <div className="checkout-cart__row promo">
                <span className="checkout-cart__item">Promotion</span>
                <span className="checkout-cart__desc">${coupon} off</span>
              </div>
            ) : null}
            <div className="checkout-cart__row occurrence">
              <span className="checkout-cart__item">Occurrence</span>
              <span className="checkout-cart__desc">{occurrence}</span>
            </div>
            {freetrial ? (
              <div>
                <div className="checkout-cart__row promo">
                  <span className="checkout-cart__item">Free Trial</span>
                  <span className="checkout-cart__desc">
                    {trialDuration + ' Days'}
                  </span>
                </div>
                <p className="checkout-cart__renewal">
                  {'If you don\'t cancel your subscription before the trial ends on ' + trialEndDate + ', you agree that you will automatically be charged the ' + occurrence + ' subscription fee until you cancel. Full terms are available '}
                  <a href="http://www.benzinga.com/terms-and-conditions" target="_blank">here</a>
                </p>
              </div>
            ) : null}
            <div className="checkout-cart__row total">
              <span className="checkout-cart__item">Today's Total</span>
              <span className="checkout-cart__desc">${freetrial ? 0 : price}</span>
            </div>
          </section>
          {this.handleError('submit')}
          <section className="checkout-section proceed">
            <button className={submitClasses} disabled={!this.validate()} onClick={() => this.submitCheckout()}>
              <span className="checkout-btn__text">{'START YOUR ' + trialDuration + '-DAY TRIAL NOW'}</span>
              <i className="checkout-btn__spinner"></i>
            </button>
          </section>
        </div>
      </div>
    );
  }
}

Checkout.propTypes = {
  params: React.PropTypes.object,
  session: React.PropTypes.object,
  billing: React.PropTypes.object,
  dispatch: React.PropTypes.func.isRequired
};

function selectUser(state) {
  return {
    session: state.session,
    billing: state.billing
  };
}

reactMixin.onClass(Checkout, History);
export default connect(selectUser)(Checkout);
