import * as React from 'react'
import { connect } from 'react-redux'
import { FormFieldWrapper, BaseFormField } from '.'
import { FORM_REDUCER_KEY } from '../reducers'
import { mapStateToField } from '../selectors'
import { NumberInputProps, NumberInputComponentProps, AbstractFormField } from '../interfaces'
import { setFormField } from '../actions'

const styles = require('../../src/styles/components/forms.scss')

/**
 * NOTE: you can't really have a number input in React
 * because of the error message of changing a controlled
 * component to an uncontrolled component. This happens because
 * undefined is not a valid starting value for a field
 * Instead it is much easier to use a string text field (which uses
 * the empty string as a valid starting input) and then to parse
 * the string as an int. This also gets round the problem that
 * e is a valid number in html
 */
class NumberInputComponent extends BaseFormField<number> {

    public renderField() {
        const { name, disabled, placeholder } = this.props

        return (
            <input
                ref={this.setFieldRef}
                name={name}
                id={`${name}Field`}
                className={this.classNames()}
                placeholder={placeholder}
                type="text"
                value={this.getValueFromState()}
                disabled={disabled}
                onChange={this.onChange}
                onFocus={this.onFocus}
                onBlur={this.onBlur} />
        )
    }

    public getValueFromEvent(e: React.SyntheticEvent<any>) {
        const val = e.currentTarget.value

        // handy trick to parse as either int or float
        const parsed = val * 1

        if (Number.isNaN(parsed)) {
            // the user could have added an invalid char
            // at the end of a sequence of valid chars - so we need
            // to go back and check the last know value
            const oldValue = this.getValueFromState()

            // if the last known value weas the empty string then
            // we should convert it to undefined so it doesn't violate
            // type rules in the state tree
            return oldValue || undefined
        }

        return parsed
    }

    public getValueFromState() {
        const { value, onChange, field } = this.props

        // the value we get from the state will be an int or float
        const res = value && onChange ? value : field.value

        // the value we give to the input field must be a string
        return res ? res.toString() : ''
    }
}

const mapStateToProps = (state: any, ownProps: NumberInputProps) => ({
    ...ownProps,
    field: mapStateToField(state[FORM_REDUCER_KEY], ownProps),
})

const mapDispatchToProps = {
    setFormField,
}

const ConnectedNumberInput = connect(mapStateToProps, mapDispatchToProps)(NumberInputComponent)

export class NumberInput extends FormFieldWrapper<NumberInputProps> {
    public render() {
        return (
            <ConnectedNumberInput {...this.props} formName={this.formName} />
        )
    }
}
