import React from 'react';
import classnames from 'classnames';
import * as utils from 'components/payment/utils';
import * as constants from '../constants';


class CCExpiryInput extends React.Component {
  constructor() {
    super();
    this.state = {
      value: '',
    };

    this.updateState = this.updateState.bind(this);
  }

  /**
   * onKeyDown event handler.
   */
  onKeyDown(e) {
    this.formatBackExpiry(e);
  }

  /**
   * onInput event handler.
   */
  onInput(e) {
    this.reformatExpiry(e);
  }

  /**
   * onKeyPress event handler.
   */
  onKeyPress(e) {
    utils.restrictNumeric(e);
    this.restrictExpiry(e);
    this.formatExpiry(e);
    this.formatForwardSlashAndSpace(e);
    this.formatForwardExpiry(e);
  }

  /**
   * Given an expiration date, return it formatted nicely.
   * @param  {string} expiry
   * @return {string}        [description]
   */
  getFormattedExpiry(expiry) {
    const parts = expiry.match(/^\D*(\d{1,2})(\D+)?(\d{1,4})?/); // lel

    if (!parts) {
      // Nothing to do. Bail.
      return '';
    }

    let mon = parts[1] || '';
    let sep = parts[2] || '';
    const year = parts[3] || '';

    if (year.length > 0) {
      sep = ' / ';
    } else if (sep === ' /') {
      mon = mon.substring(0, 1);
      sep = '';
    } else if (mon.length === 2 || sep.length > 0) {
      sep = ' / ';
    } else if (mon.length === 1 &&
                (mon !== '0' && mon !== '1')) {
      mon = '0' + mon;
      sep = ' / ';
    }

    return mon + sep + year;
  }

  updateState(value) {
    this.setState({value: value});
    this.props.onStateUpdate({exp: value});
  }

  /**
   * Given an event, format the expiration date.
   */
  formatExpiry(e) {
    const digit = String.fromCharCode(e.which);

    // Only format numerics
    if (!/^\d+$/.test(digit)) {
      return;
    }

    const target = e.target;
    const value = target.value + digit;

    if (/^\d$/.test(value) && (value !== '0' && value !== '1')) {
      // Matches if the user enters only a single digit (ex. '4', for April)
      e.preventDefault();
      this.updateState(`0${value} / `);
    } else if (/^\d\d$/.test(value)) {
      // Split for months where we have the second digit > 2 (past 12) and turn
      // that into (m1)(m2) => 0(m1) / (m2)
      e.preventDefault();
      let m1;
      let m2;
      m1 = parseInt(value[0], 10);
      m2 = parseInt(value[1], 10);
      if (m2 > 2 && m1 !== 0) {
        this.updateState(`0${m1} / ${m2}`);
      } else {
        this.updateState(`${value} / `);
      }
    }
  }

  /**
   * Add the ' / ' during input
   */
  formatForwardSlashAndSpace(e) {
    const which = String.fromCharCode(e.which);

    // Only format space and slash characters
    if (which !== '/' && which !== ' ') {
      return;
    }

    const target = e.target;
    const value = target.value;

    if (/^\d$/.test(value) && value !== '0') {
      this.updateState('0' + value + ' / ');
    }
  }

  formatForwardExpiry(e) {
    const digit = String.fromCharCode(e.which);

    if (!/^\d+$/.test(digit)) {
      return;
    }

    const target = e.target;
    const value  = target.value;

    if (/^\d\d$/.test(value)) {
      this.updateState(value + ' / ');
    }
  }

  formatBackExpiry(e) {
    const target = e.target;
    const value = target.value;

    // Return unless backspacing
    if (e.which !== 8) {
      return;
    }

    // Return if focus isnt at the end of the text
    if (target.selectionStart !== null && target.selectionStart !== value.length) {
      return;
    }

    // Remove the trailing space and last digit
    // '04 / ' -> '0'
    if (/\d\s\/\s$/.test(value)) {
      e.preventDefault();
      this.updateState(value.replace(/\d\s\/\s$/, ''));
    }
  }

  /**
   * Forces a reformat on the expiration date in state.
   */
  reformatExpiry(e) {
    const target = e.target;
    let value = target.value;

    value = this.getFormattedExpiry(value);
    this.updateState(value);
  }

  /**
   * Returns an object of the card's expiration date
   * CURRENTLY ONLY BEING USED FOR TESTS
   */
  cardExpiryVal() {
    const value = this.state.value.replace(/\s/g, '');
    const monthYear = value.split('/', 2);

    if (monthYear.length === 2) {
      let month = monthYear[0];
      let year = monthYear[1];

      // Allow for year shortcut
      if (year.length === 2 && /^\d+$/.test(year)) {
        let prefix = (new Date()).getFullYear();
        prefix = prefix.toString().slice(0, 2);
        year = prefix + year;
      }

      month = parseInt(month, 10);
      year = parseInt(year, 10);

      return {month: month, year: year};
    }
  }

  // CURRENTLY ONLY BEING USED FOR TESTS
  validate() {
    const expiryVal = this.cardExpiryVal();

    // Expect an object
    if (typeof expiryVal !== 'object' || !expiryVal.month  || !expiryVal.year) {
      return false;
    }

    const month = expiryVal.month.toString();
    const year  = expiryVal.year.toString();

    if (!(month >= 1 && month <= 12)) {
      return false;
    }

    const expiry = new Date(year, month);
    const currentTime = new Date;

    // Months start from 0 in JavaScript
    expiry.setMonth(expiry.getMonth() - 1);

    // The cc expires at the end of the month,
    // we need to make the expiry the first day
    // of the month after
    expiry.setMonth(expiry.getMonth() + 1, 1);
    return expiry > currentTime;
  }

  /**
   * Field restrictions on input
   * @param  {Object} e HTML event
   * @return {Boolean}  Restrict this event?
   */
  restrictExpiry(e) {
    const target = e.target;
    const digit = String.fromCharCode(e.which);

    if (!/^\d+$/.test(digit)) {
      return true;
    }

    if (utils.hasTextSelected(target)) {
      return true;
    }

    let value = target.value + digit;
    value = value.replace(/\D/g, '');

    if (value.length > 6) {
      e.preventDefault();
      return false;
    }
  }

  render() {
    const value = this.state.value;
    const { disabled, error } = this.props;
    const fieldClasses = classnames('checkout-field', 'ccexpire', {error: error});

    return (
      <div className={fieldClasses}>
        <label className="checkout-field__label">EXPIRATION</label>
        <input className="checkout-field__input"
          type="text"
          autoComplete="cc-expiration"
          ref="ccExpiryInput"
          value={value}
          onKeyPress={(e) => this.onKeyPress(e)}
          onKeyDown={(e) => this.onKeyDown(e)}
          onInput={(e) => this.onInput(e)}
          disabled={disabled}
        />
        {error ? (<div className="checkout-field__error">{error}</div>) : null}
      </div>
    );
  }
}

CCExpiryInput.propTypes = {
  onStateUpdate: React.PropTypes.func,
  error: React.PropTypes.string,
  disabled: React.PropTypes.bool,
};

export default CCExpiryInput;
