import React from 'react';
import ReactDOM from 'react-dom';
import TextInput from '../../atoms/text-input';
import Dropdown from '../../atoms/dropdown';
import { ModelListener } from 'front';
import BankAccountModel, {
    ACCOUNT_TYPE_BUSINESS
} from './bank-account.js';
import FormValidation, {
    createSyncChecker
} from '../../transforms/form-validation';
import { matcher } from '../../etc/util';
import './bank-account.less';

let DigitsFormatter;

/* istanbul ignore if: environment specific */
if (typeof document !== 'undefined') {
    DigitsFormatter = require('../../etc/digits-formatter').default;
}

/**\

    title: BankAccount
    category: component
    description: A standard bank account form
    props:
        nickname[Boolean]:
            Adds a text field to allow entering a label for the bank account.
            Default is false.
        inlineErrors[Boolean]:
            Show inline errors for invalid fields. Default if false. Error
            messages can always be accessed through <BankAccount>.model.errors.
        native[Boolean]:
            whether the dropdown for account type uses native select/option

\**/
class BankAccount extends ModelListener {
    static defaultProps = {
        model: BankAccountModel
    };

    // To override the default error messages, use the setErrorMessages method
    // with a similarly structured object
    static defaultErrorMessages = {
        accountHolder: {
            required: 'Enter the name of the account holder.',
            incomplete: 'Both first and last name are required.'
        },
        taxId: {
            ssn: 'Enter a valid Social Security Number.',
            ein: 'Enter a valid Employer Identification Number.'
        },
        accountType: 'Choose an account type.',
        accountNumber: {
            required: 'Enter the account number.',
            mismatch: 'Account numbers don\'t match.'
        },
        routingNumber: 'Enter a valid routing number.'
    };

    static makeFieldValidator = function(errorMessages) {
        const getErrorMessages = matcher(
            errorMessages,
            BankAccount.defaultErrorMessages,
            null);

        return function(fieldname, value) {
            switch (fieldname) {
                case 'ssn':
                case 'ein':
                    if (!value || value.length < 9) {
                        return getErrorMessages('taxId')[fieldname];
                    }
                    break;
                case 'accountNumber':
                    if (!value) {
                        return getErrorMessages(fieldname).required;
                    }
                    break;
                case 'accountHolder':
                    if (!value) {
                        return getErrorMessages(fieldname).required;
                    }
                    if (value.trim().split(' ').length < 2) {
                        return getErrorMessages(fieldname).incomplete;
                    }
                    break;
                case 'routingNumber':
                    if (!BankAccountModel.validateRoutingNumber(value)) {
                        return getErrorMessages(fieldname);
                    }
                    break;
                case 'accountType':
                    if (!value) {
                        return getErrorMessages(fieldname);
                    }
                    break;
                default:
                    return null;
            }

            return null;
        };
    }

    constructor(props) {
        super(props);

        this.checker = this.makeChecker();
        FormValidation(this, this.checker);

        this.model.onUpdate('accountType', () => {
            if (this.model.errors.taxId) {
                this.checker('taxId', (msg) => {
                    this.model.errors.taxId = msg;
                    this.forceUpdate();
                });
            }
        });
    }

    setErrorMessages(errorMessages) {
        this.checker = this.makeChecker(errorMessages);
        this.formValidate.settings.setChecker(this.checker);
    }

    // A checker that also checks if both account number entries match
    makeChecker(errorMessages = {}) {
        const validator = BankAccount.makeFieldValidator(errorMessages);
        let msg = BankAccount.defaultErrorMessages.accountNumber.mismatch;

        // use custom error message for mismatch if provided
        if (typeof errorMessages.accountNumber === 'object' &&
            errorMessages.accountNumber.mismatch) {
            msg = errorMessages.accountNumber.mismatch;
        }

        return createSyncChecker(this, (fieldname, prop) => {
            let _fieldname = fieldname;

            if (fieldname === 'accountNumber' && !this.match(prop)) {
                return msg;
            }

            if (fieldname === 'taxId') {
                _fieldname =
                    this.model.accountType === ACCOUNT_TYPE_BUSINESS ?
                    'ein' : 'ssn';
            }

            return validator(_fieldname, prop);
        });
    }

    componentDidMount() {
        super.componentDidMount();
        this.setFormatters();

        this.confirmation = ReactDOM
            .findDOMNode(this._confirmation)
            .getElementsByTagName('input')[0];
    }

    setFormatters() {
        const routingNumberFormatter = new DigitsFormatter(),
            accountNumberFormatter = new DigitsFormatter();

        routingNumberFormatter.maximumLength = 9;
        accountNumberFormatter.maximumLength = 20;

        this._routing.field.setFormatter(routingNumberFormatter);
        this._account.field.setFormatter(accountNumberFormatter);
        this._confirmation.field.setFormatter(accountNumberFormatter);
    }

    match(value, confirmation) {
        const _value = value || this.model.accountNumber,
            _confirmation = confirmation || this.confirmation.value;

        return _value === _confirmation;
    }

    onConfirmationUpdate() {
        // if there are no existing errors, don't bother checking
        if (!this.model.errors.accountNumber) {
            return;
        }

        this.checker('accountNumber', (msg) => {
            // update the error message if it changed
            if (this.model.errors.accountNumber !== msg) {
                this.model.errors.accountNumber = msg;
                this.forceUpdate();
            }
        });
    }

    render() {
        let errors = {},
            nickname,
            taxIdPlaceholder = 'Social Security Number';

        if (this.props.inlineErrors) {
            errors = this.model.errors;
        }

        if (this.props.nickname) {
            nickname = (
                <div className="row t100">
                    <TextInput
                        placeholder="Nickname"
                        altPlaceholder="Personal"
                        value={ this.model.label }
                        update={ this.update.bind(this, 'label') } />
                </div>
            );
        }

        if (this.model.accountType === ACCOUNT_TYPE_BUSINESS) {
            taxIdPlaceholder = 'Employer Identification Number';
        }

        return (
            <div className="bank-account">
                { nickname }

                <div className="row t100">
                    <Dropdown
                        placeholder="Account Type"
                        native={ this.props.native }
                        items={ BankAccountModel.accountTypes }
                        value={ this.model.accountType }
                        onSelect={
                            (option) => this.update('accountType', option.value)
                        }
                        error={ errors.accountType } />
                </div>
                <div className="row t100">
                    <TextInput
                        error={ errors.accountHolder }
                        placeholder="Name on Account"
                        value={ this.model.accountHolder }
                        update={ this.update.bind(this, 'accountHolder') } />
                </div>
                <div className="row t100">
                    <TextInput
                        format="ssn"
                        ref={ (n) => (this._taxId = n) }
                        error={ errors.taxId }
                        placeholder={ taxIdPlaceholder }
                        altPlaceholder="000-00-0000"
                        value={ this.model.taxId }
                        update={ this.update.bind(this, 'taxId') } />
                </div>
                <div className="row t100">
                    <TextInput
                        ref={ (n) => (this._routing = n) }
                        error={ errors.routingNumber }
                        className="routing-number"
                        placeholder="Routing Number"
                        altPlaceholder="000000000"
                        value={ this.model.routingNumber }
                        update={ this.update.bind(this, 'routingNumber') } />
                </div>
                <div className="row t100">
                    <TextInput
                        ref={ (n) => (this._account = n) }
                        error={ errors.accountNumber }
                        className="account-number"
                        placeholder="Account Number"
                        value={ this.model.accountNumber }
                        update={ this.update.bind(this, 'accountNumber') } />
                </div>
                <div className="row t100">
                    <TextInput
                        ref={ (n) => (this._confirmation = n) }
                        className="account-number"
                        error={ errors.accountNumber }
                        placeholder="Confirm Account Number"
                        update={ this.onConfirmationUpdate.bind(this) } />
                </div>
            </div>
        );
    }
}

export default BankAccount;
