import * as React from 'react'
import { } from './interfaces'
import * as cx from 'classnames'
import { connect } from 'react-redux'
import { WebFormField, FormFieldComponentProps, FormFieldWrapper } from '../form-field'
import { setFormField, mapStateToField, FORM_REDUCER_KEY } from '../form'
import { SelectProps, SelectComponentProps, SelectOption } from './interfaces'

const styles = require('../../../src/components/form-field/styles.scss')
const borderStyles = require('../../../src/components/styles/globals/borders.scss')

/**
 * WARNING: React provides no support for using an object as the value of an option.
 * The SelectOption interface has been provided to handle this issue.
 * If you pass an array of objects as options, then each option must implement the
 * SelectOption interface. The label will be the text that is visible to the user
 */
class SelectComponent extends WebFormField<any> {

    private optionsAreObjects: boolean

    constructor(props: FormFieldComponentProps<any>) {
        super(props)
        this.setOptionType()
    }

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

        return (
            <select
                ref={this.setFieldRef}
                value={this.getSelectedValue()}
                name={name}
                id={`${name}Field`}
                disabled={disabled}
                className={this.classNames(styles.select)}
                onChange={this.onChange}
                onFocus={this.onFocus}
                onBlur={this.onBlur}>
                {this.renderOptions()}
            </select>
        )
    }

    public getValueFromEvent(e: React.SyntheticEvent<any>) {
        let selectedOption = e.currentTarget.value
        // if the options are objects
        if (this.optionsAreObjects) {
            // then return the entire object associated with this label
            selectedOption = (this.props as SelectComponentProps).options.find(o =>
                (o as SelectOption).label === e.currentTarget.value)
        }
        return selectedOption
    }

    private getSelectedValue(): string | number {
        const value = this.getValueFromState()

        if (this.optionsAreObjects) {
            return value && value.label ? value.label : ''
        }
        return value
    }

    private renderOptions(): any[] {
        const { options } = (this.props as SelectComponentProps)

        let res: any[]

        if (this.optionsAreObjects) {
            res = options.map(o =>
                <option value={o.label} key={o.label}>{o.label}</option>,
            )
        } else {
            res = options.map(o =>
                <option value={o} key={o}>{o}</option>,
            )
        }
        // add a "Please select" option to the start
        if (!this.props.value && !this.props.field.value) {
            res.unshift(
                <option value={''} key={`${this.props.name}StarterKey`}>
                    Please select
                </option>,
            )
        }
        return res
    }

    private setOptionType() {
        const { options } = (this.props as SelectComponentProps)
        this.optionsAreObjects = options[0] && (typeof options[0] === 'object')
    }

}

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

const mapDispatchToProps = {
    setFormField,
}

// TODO - there's a type issue here because options is compoulsory on SelectProps
const ConnectedSelect = connect(mapStateToProps, mapDispatchToProps)(SelectComponent as any)

export class Select extends FormFieldWrapper<SelectProps> {
    public render() {
        return <ConnectedSelect {...this.props} formName={this.formName} />
    }
}
