import { colors } from '@0xproject/react-shared';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import TextField from 'material-ui/TextField';
import * as React from 'react';
import { RequiredLabel } from 'ts/components/ui/required_label';
import { ValidatedBigNumberCallback } from 'ts/types';
import { utils } from 'ts/utils/utils';

interface BalanceBoundedInputProps {
    label?: string;
    balance: BigNumber;
    amount?: BigNumber;
    hintText?: string;
    onChange: ValidatedBigNumberCallback;
    onErrorMsgChange?: (errorMsg: React.ReactNode) => void;
    shouldShowIncompleteErrs?: boolean;
    shouldCheckBalance: boolean;
    validate?: (amount: BigNumber) => React.ReactNode;
    isDisabled?: boolean;
    shouldShowErrs?: boolean;
    shouldShowUnderline?: boolean;
    inputStyle?: React.CSSProperties;
    inputHintStyle?: React.CSSProperties;
}

interface BalanceBoundedInputState {
    errMsg: React.ReactNode;
    amountString: string;
}

export class BalanceBoundedInput extends React.Component<BalanceBoundedInputProps, BalanceBoundedInputState> {
    public static defaultProps: Partial<BalanceBoundedInputProps> = {
        shouldShowIncompleteErrs: false,
        isDisabled: false,
        shouldShowErrs: true,
        hintText: 'amount',
        onErrorMsgChange: _.noop.bind(_),
        shouldShowUnderline: true,
    };
    constructor(props: BalanceBoundedInputProps) {
        super(props);
        const amountString = this.props.amount ? this.props.amount.toString() : '';
        this.state = {
            errMsg: this._validate(amountString, props.balance),
            amountString,
        };
    }
    public componentWillReceiveProps(nextProps: BalanceBoundedInputProps): void {
        if (nextProps === this.props) {
            return;
        }
        const isCurrentAmountNumeric = utils.isNumeric(this.state.amountString);
        if (!_.isUndefined(nextProps.amount)) {
            let shouldResetState = false;
            if (!isCurrentAmountNumeric) {
                shouldResetState = true;
            } else {
                const currentAmount = new BigNumber(this.state.amountString);
                if (!currentAmount.eq(nextProps.amount) || !nextProps.balance.eq(this.props.balance)) {
                    shouldResetState = true;
                }
            }
            if (shouldResetState) {
                const amountString = nextProps.amount.toString();
                this._setAmountState(amountString, nextProps.balance);
            }
        } else if (isCurrentAmountNumeric) {
            const amountString = '';
            this._setAmountState(amountString, nextProps.balance);
        }
    }
    public render(): React.ReactNode {
        let errorText;
        if (this.props.shouldShowErrs) {
            errorText =
                this.props.shouldShowIncompleteErrs && this.state.amountString === ''
                    ? 'This field is required'
                    : this.state.errMsg;
        }
        let label: React.ReactNode | string = '';
        if (!_.isUndefined(this.props.label)) {
            label = <RequiredLabel label={this.props.label} />;
        }
        return (
            <TextField
                fullWidth={true}
                floatingLabelText={label}
                floatingLabelFixed={true}
                floatingLabelStyle={{ color: colors.grey, width: 206 }}
                errorText={errorText}
                value={this.state.amountString}
                hintText={<span style={{ textTransform: 'capitalize' }}>{this.props.hintText}</span>}
                onChange={this._onValueChange.bind(this)}
                underlineStyle={{ width: 'calc(100% + 50px)' }}
                inputStyle={this.props.inputStyle}
                hintStyle={this.props.inputHintStyle}
                underlineShow={this.props.shouldShowUnderline}
                disabled={this.props.isDisabled}
            />
        );
    }
    private _onValueChange(_event: any, amountString: string): void {
        this._setAmountState(amountString, this.props.balance, () => {
            const isValid = _.isUndefined(this._validate(amountString, this.props.balance));
            const isPositiveNumber = utils.isNumeric(amountString) && !_.includes(amountString, '-');
            if (isPositiveNumber) {
                this.props.onChange(isValid, new BigNumber(amountString));
            } else {
                this.props.onChange(isValid);
            }
        });
    }
    private _validate(amountString: string, balance: BigNumber): React.ReactNode {
        if (!utils.isNumeric(amountString)) {
            return amountString !== '' ? 'Must be a number' : '';
        }
        const amount = new BigNumber(amountString);
        if (amount.eq(0)) {
            return 'Cannot be zero';
        }
        if (this.props.shouldCheckBalance && amount.gt(balance)) {
            return <span>Insufficient balance.</span>;
        }
        const errMsg = _.isUndefined(this.props.validate) ? undefined : this.props.validate(amount);
        return errMsg;
    }
    private _setAmountState(amount: string, balance: BigNumber, callback: () => void = _.noop.bind(_)): void {
        const errorMsg = this._validate(amount, balance);
        this.props.onErrorMsgChange(errorMsg);
        this.setState(
            {
                amountString: amount,
                errMsg: errorMsg,
            },
            callback,
        );
    }
}
