import React from 'react';
import { matcher } from '../../etc/util';
import './text-input.less';

let FieldKit,
    CardFormatter,
    ExpiryDateFormatter,
    PhoneFormatter,
    SSNFormatter,
    TextFormatter,
    CVVFormatter,
    OnlyNumberFormatter,
    OnlyMoneyFormatter,
    PercentFormatter;

if (typeof document !== 'undefined') {
    FieldKit = require('field-kit/dist/field-kit');
    CardFormatter = FieldKit.AdaptiveCardFormatter;
    ExpiryDateFormatter = FieldKit.ExpiryDateFormatter;
    PhoneFormatter = FieldKit.PhoneFormatter;
    SSNFormatter = FieldKit.SocialSecurityNumberFormatter;
    TextFormatter = FieldKit.Formatter;
    CVVFormatter = require('../../etc/cvv-formatter').default;
    OnlyNumberFormatter = require('../../etc/only-number-formatter').default;
    OnlyMoneyFormatter = require('../../etc/only-money-formatter').default;
    PercentFormatter = require('../../etc/percent-formatter').default;
}


export const defaultPlaceholders = {
    card: 'Card Number',
    cvv: 'CVV',
    email: 'Email Address',
    expiration: 'Exp. Date',
    password: 'Password',
    phone: 'Phone Number',
    ssn: 'Social Security Number',
    website: 'Website',
    percent: 'Rate'
};

export const defaultAltPlaceholders = {
    card: '0000 0000 0000 0000',
    email: 'you@example.com',
    expiration: 'MM/YY',
    phone: '(000) 000-0000',
    ssn: '000-00-0000',
    website: 'www.example.com',
    percent: '00.00%'
};

// NOTE(wezleytsai): type 'number' and 'email' will result in
// InvalidStateError due to FieldKit accessing a non-standard property
// related: https://github.com/firstopinion/formatter.js/issues/29
export const defaultType = {
    password: 'password',
    phone: 'tel',
    website: 'url'
};

export const defaultInputMode = {
    card: 'numeric',
    email: 'email',
    ssn: 'numeric'
};

/**\

    title: TextInput
    category: atom
    description: A text input with the ability to be bound both ways
    props:
        className[String]: Classes to pass down to this component
        label[String]: A label to display above the textarea
        focus[Function]: A callback for the focus event
        blur[Function]: A callback for the blur event
        placeholder[String]:
            Some text to display when the textarea has no value
        altPlaceholder[String]:
            Some text to display when the textarea has no value and has focus.
            If not defined, the placeholder is used
        value[String]: The value of the textarea
        update[Function]: A callback for the textarea's onChange event
        error[String]: Field specific error text
        setFocus[Boolean]: Sets focus on initial render if true
        disabled[Boolean]: Disables the element
        format[String]:
            options: (text|email|password|number|float|money|phone|card|ssn|
            website|expiration|cvv|percent)
            Sets the formatter on the input which defines what is
            acceptable input values. "expiration" format accepts values in the
            form of { month, year }.
        autoCapitalize[none|sentences|words|characters]: autocapitalize attr

\**/
class TextInput extends React.Component {
    static getPlaceholder = matcher(defaultPlaceholders, '');
    static getAltPlaceholder = matcher(defaultAltPlaceholders, '');
    static getType = matcher(defaultType, 'text');
    static getInputMode = matcher(defaultInputMode, null);

    state = {
        focused: false,
        masked: true
    };

    componentDidMount() {
        this.field = new FieldKit.TextField(this.input, this.getFormatter());
        this.field.setValue(this.props.value || '');
        this.field.setDelegate({
            textDidChange: this.update.bind(this),
            textFieldDidEndEditing: this.update.bind(this)
        });

        if (this.props.setFocus) {
            this.input.focus();
            this.onFocus();
        }
    }

    componentWillReceiveProps(props) {
        if (props.hasOwnProperty('format') &&
            props.format !== this.props.format) {
            this.field.setFormatter(this.getFormatter(props));
        }

        if (props.hasOwnProperty('value')) {
            if (!this.state.focused || !this.field.value()) {
                this.field.setValue(props.value);
            }
        }
    }

    getAutoCapitalize(format) {
        const allowedFormats = [ 'text' ],
            { autoCapitalize } = this.props;

        return allowedFormats.indexOf(format) !== -1 ? autoCapitalize : null;
    }

    getFormatter(props) {
        const _props = props || this.props;

        switch (_props.format) {
            case 'card':
                return new CardFormatter();
            case 'cvv':
                return new CVVFormatter();
            case 'expiration':
                return new ExpiryDateFormatter();
            case 'phone':
                return new PhoneFormatter();
            case 'ssn':
                return new SSNFormatter();
            case 'money':
                return new OnlyMoneyFormatter();
            case 'number':
                return new OnlyNumberFormatter().setAllowsFloats(false);
            case 'float':
                return new OnlyNumberFormatter();
            case 'percent':
                return new PercentFormatter();
            default:
                return new TextFormatter();
        }
    }

    update(evt) {
        if (typeof this.props.update === 'function') {
            this.props.update(evt.value());
        }
    }

    toggle() {
        this.setState({
            masked: !this.state.masked
        });
    }

    preventDefault(evt) {
        evt.preventDefault();
    }

    onFocus() {
        this.setState({
            focused: true
        });

        if (typeof this.props.focus === 'function') {
            this.props.focus(this);
        }
    }

    onBlur() {
        this.setState({
            focused: false
        });


        if (typeof this.props.blur === 'function') {
            this.props.blur(this);
        }
    }

    render() {
        const classer = [ 'text-input' ],
            {
                placeholder,
                altPlaceholder,
                format,
                disabled,
                value,
                error,
                className,
                label
            } = this.props,
            {
                focused,
                masked
            } = this.state,
            inputMode = TextInput.getInputMode(format),
            _placeholder = placeholder || TextInput.getPlaceholder(format),
            _alt = altPlaceholder || placeholder || TextInput.getAltPlaceholder(format), // eslint-disable-line
            autoCapitalize = this.getAutoCapitalize(format);

        let type = TextInput.getType(format),
            errorTooltip = null,
            passwordToggler = null;

        if (format === 'money' || format === 'number') {
            classer.push(format);
        }

        if (format === 'password') {
            // tabIndex: so it can't be tabbed to
            // onMouseDown: so it doesn't take focus
            passwordToggler = (
                <button
                    type="button"
                    tabIndex="-1"
                    onMouseDown={ this.preventDefault }
                    onClick={ this.toggle.bind(this) }>
                    { masked ? 'Show' : 'Hide' }
                </button>
            );

            if (!masked) {
                type = 'text';
            }
        }

        if (className) {
            classer.push(className);
        }

        if (disabled) {
            classer.push('disabled');
        }

        if (error) {
            classer.push('has-error');

            if (!disabled) {
                errorTooltip = (
                    <div className="error-tooltip">
                        { this.props.error }
                    </div>
                );
            }
        }

        return (
            <div className={ classer.join(' ') }>
                { label ? <label>{ label }</label> : null }
                <div>
                    <input
                        ref={ (n) => (this.input = n) }
                        type={ type }
                        autoCapitalize={ autoCapitalize }
                        inputMode={ inputMode }
                        defaultValue={ value }
                        placeholder={ focused ? _alt : _placeholder }
                        onFocus={ this.onFocus.bind(this) }
                        onBlur={ this.onBlur.bind(this) }
                        disabled={ disabled } />
                    { passwordToggler }
                    { errorTooltip }
                </div>
            </div>
        );
    }
}

export default TextInput;
