import React from 'react'
import PropTypes from 'prop-types'
import currencies from 'common-fe/constants/currencies'
import inputNames from 'common-fe/constants/input-names'
import FormError, {reason} from 'common-fe/utils/form-error'
import {getCurrency} from 'utils/portfolio'
import {renderCash} from 'common-fe/utils/render'
import {isValidNumber, validateInput} from 'common-fe/utils/validation'
import BEMModule from 'utils/bem'
import styles from './styles.scss'

import Form from 'react-toolkit/form'
import Icon from 'react-uikit/icon'
import Button from 'react-uikit/button'
import SelectInput from 'react-uikit/select-input'
import TextInput from 'react-uikit/input-text'
import {Grid} from 'react-uikit/layout'

const bem = new BEMModule(styles)

const FLAG_ICONS = {
    USD: 'flag-usa',
    CAD: 'flag-canada',
    GBP: 'flag-uk',
}

const getNext = (length, i) => {
    return i >= length - 1 ? 0 : i + 1
}

const currencyList = Object.keys(currencies).map((id) => {
    let label = null

    switch (id) {
        case 'CAD':
            label = 'Canadian Dollar'
            break

        case 'USD':
            label = 'U.S. Dollar'
            break

        case 'GBP':
            label = 'Pound Sterling'
            break

        default:
            return undefined
    }

    return {value: id, label: `${id} ${currencies[id]} – ${label}`}
})

class CurrencyExchangeForm extends React.Component {
    static formName = 'currency-exchange'

    static currencyList = currencyList

    static defaultState = {
        data: {
            [inputNames.CX_FROM]: CurrencyExchangeForm.currencyList[2],
            [inputNames.CX_TO]: CurrencyExchangeForm.currencyList[0],
            [inputNames.CX_FROM_QUANTITY]: '',
            [inputNames.CX_TO_QUANTITY]: '',
        },
        error: {},
    }

    form = React.createRef()

    get initialState() {
        return {
            data: {...CurrencyExchangeForm.defaultState.data},
            error: {...CurrencyExchangeForm.defaultState.error},
        }
    }

    get rate() {
        const {currencies, data} = this.props
        const toCurrency = currencies[data[inputNames.CX_TO].value]
        const fromCurrency = currencies[data[inputNames.CX_FROM].value]

        if (!toCurrency || !fromCurrency) {
            return null
        }

        return (1 / fromCurrency.currentPrice) * toCurrency.currentPrice
    }

    componentDidMount() {
        this.props.getAllCurrencies && this.props.getAllCurrencies()
    }

    change = ({name, value}) => {
        const {onChange} = this.props
        const change = {[name]: value}
        switch (name) {
            case inputNames.CX_FROM:
            case inputNames.CX_TO: {
                // (1) Find the opposite transaction.
                const opposingDirection =
                    name === inputNames.CX_FROM
                        ? inputNames.CX_TO
                        : inputNames.CX_FROM

                // (2) If the user is trying to trade the same currency...
                if (value.value === this.props.data[opposingDirection]?.value) {
                    // (2.1) ...reset the quantities,
                    change[inputNames.CX_FROM_QUANTITY] = ''
                    change[inputNames.CX_TO_QUANTITY] = ''
                    // (2.2) ...set the opposing currency to the next one.
                    const currentIndex = currencyList.findIndex(
                        (currency) =>
                            currency.value ===
                            this.props.data[opposingDirection]?.value
                    )
                    const nextIndex = getNext(currencyList.length, currentIndex)
                    change[opposingDirection] = currencyList[nextIndex]
                }

                break
            }

            case inputNames.CX_FROM_QUANTITY: {
                change[inputNames.CX_FROM_QUANTITY] = value
                change[inputNames.CX_TO_QUANTITY] =
                    isValidNumber(value) && typeof this.rate === 'number'
                        ? renderCash(value * this.rate, {round: 'floor'})
                        : ''
                break
            }

            case inputNames.CX_TO_QUANTITY: {
                change[inputNames.CX_TO_QUANTITY] = value
                change[inputNames.CX_FROM_QUANTITY] =
                    isValidNumber(value) && typeof this.rate === 'number'
                        ? renderCash(value / this.rate, {round: 'floor'})
                        : ''
                break
            }
        }

        onChange && onChange(change)
    }

    update = ({name, value}) => {
        const {data} = this.props
        const change = {name, value}

        switch (name) {
            case inputNames.CX_FROM:
            case inputNames.CX_TO: {
                // Refetch selected currencies when inputs change in case
                // rates have changed since (maybe throttle this?).
                const isFromCurrencyId = name === inputNames.CX_FROM
                const fromCurrencyId = isFromCurrencyId
                    ? value.value
                    : data[inputNames.CX_FROM].value
                const toCurrencyId = isFromCurrencyId
                    ? data[inputNames.CX_TO].value
                    : value.value

                const currencyIds = [...new Set([fromCurrencyId, toCurrencyId])]
                this.props.getCurrencies &&
                    this.props.getCurrencies({currencyIds})
                break
            }

            case inputNames.CX_FROM_QUANTITY:
            case inputNames.CX_TO_QUANTITY:
                change.value = value.length ? renderCash(value) : ''
                break
        }

        this.change(change)

        // Put at the end of the JS queue to validate once updates have propagated
        setTimeout(() => this.form.current && this.form.current.validate(), 0)
    }

    submit = ({
        [inputNames.CX_FROM]: {value: fromCurrencyId},
        [inputNames.CX_TO]: {value: toCurrencyId},
        [inputNames.CX_FROM_QUANTITY]: fromQuantity,
        [inputNames.CX_TO_QUANTITY]: toQuantity,
    }) => {
        const {exchangeCurrency, portfolio, onError, onSubmit} = this.props

        const payload = {
            portfolioId: portfolio.id,
            fromCurrencyId,
            fromQuantity,
            toCurrencyId,
            toQuantity,
        }

        exchangeCurrency &&
            exchangeCurrency(payload)
                .then(() => onSubmit && onSubmit(payload))
                .catch((err) => {
                    if (
                        !err.inlineErrors ||
                        !err.inlineErrors.length ||
                        !onError
                    ) {
                        return
                    }

                    const error = new FormError(
                        Object.keys(CurrencyExchangeForm.defaultState.data),
                        err.inlineErrors
                    )

                    onError && onError(error)
                })
    }

    validate = ({name, value, isRequired}) => {
        let error = validateInput({name, value, isRequired})

        switch (name) {
            case inputNames.CX_FROM_QUANTITY: {
                // If there is no value, then don't do anything
                if (!value.length) {
                    break
                }

                if (!isValidNumber(value)) {
                    error = reason.NUMBER_INVALID_ERR
                } else if (value <= 0) {
                    error = reason.VALUE_NOT_POSITIVE_ERR
                }
            }
        }

        return error
    }

    render() {
        const {data, error, portfolio, validateOnUpdate, onError} = this.props
        const {cash} = portfolio.holdings

        const modalClassNames = bem.classNames('c-currency-exchange')
        const hrClassNames = bem.classNames(
            'c-currency-exchange__hr',
            'u-grid-item--span-columns'
        )
        const totalHrClassNames = bem.classNames(
            'c-currency-exchange__total-hr'
        )
        const downIconClassNames = bem.classNames('c-currency-exchange__down')
        const multiplyClassNames = bem.classNames(
            'c-currency-exchange__multiply'
        )
        const numberClassNames = bem.classNames('c-currency-exchange__number')
        const labelClassNames = bem.classNames('c-currency-exchange__label')
        const columnLabelClassNames = bem.classNames(
            'c-currency-exchange__column-label'
        )
        const valueClassNames = bem.classNames('c-currency-exchange__value')
        const inputClassNames = bem.classNames('c-currency-exchange__input')
        const buttonClassNames = bem.classNames(
            'c-currency-exchange__button',
            'es-modal-form__button-action'
        )
        const rate =
            typeof this.rate === 'number' ? this.rate.toFixed(4) : 'N/A'

        return (
            <Form
                data={data}
                error={error}
                name={CurrencyExchangeForm.formName}
                ref={this.form}
                submitOnEnter
                validate={this.validate}
                validateOnUpdate={validateOnUpdate}
                onChange={this.change}
                onUpdate={this.update}
                onError={onError}
                onSubmit={this.submit}
            >
                <div className={modalClassNames}>
                    <Grid
                        align="center"
                        gap="1.6rem"
                        templateColumns="1fr 3fr 2fr"
                    >
                        <h4
                            className={columnLabelClassNames}
                            style={{
                                gridColumn: '3/-1',
                                justifySelf: 'end',
                            }}
                        >
                            Current Balance
                        </h4>
                        <label
                            className={labelClassNames}
                            htmlFor={inputNames.CX_FROM}
                        >
                            Selling
                        </label>
                        <Form.Field
                            component={SelectInput}
                            name={inputNames.CX_FROM}
                            options={CurrencyExchangeForm.currencyList}
                            size="small"
                        />
                        <span className={valueClassNames}>
                            {getCurrency({
                                holdings: cash,
                                currency: data[inputNames.CX_FROM].value,
                            }).replace(/\$|£/, '')}
                        </span>

                        <Icon
                            className={downIconClassNames}
                            name="chevron-down"
                        />

                        <label
                            className={labelClassNames}
                            htmlFor={inputNames.CX_TO}
                        >
                            Buying
                        </label>
                        <Form.Field
                            component={SelectInput}
                            name={inputNames.CX_TO}
                            options={CurrencyExchangeForm.currencyList}
                            size="small"
                        />
                        <span className={valueClassNames}>
                            {getCurrency({
                                holdings: cash,
                                currency: data[inputNames.CX_TO].value,
                            }).replace(/\$|£/, '')}
                        </span>
                    </Grid>

                    <hr className={hrClassNames} />

                    <Grid
                        align="center"
                        gap="1.6rem"
                        templateColumns="2fr 1fr 3fr"
                    >
                        <label
                            className={labelClassNames}
                            htmlFor={inputNames.CX_FROM_QUANTITY}
                        >
                            Sell Amount
                        </label>
                        <Form.Field
                            className={inputClassNames}
                            component={TextInput}
                            iconName={
                                FLAG_ICONS[data[inputNames.CX_FROM].value]
                            }
                            iconSize="small"
                            name={inputNames.CX_FROM_QUANTITY}
                            placeholder="0"
                            required
                            size="small"
                            type="number"
                        />

                        <p>Exchange Rate</p>
                        <div className={labelClassNames}>
                            <Icon className={multiplyClassNames} name="close" />
                        </div>
                        <p className={numberClassNames}>{rate}</p>

                        <hr className={totalHrClassNames} />

                        <label
                            className={labelClassNames}
                            htmlFor={inputNames.CX_TO_QUANTITY}
                        >
                            Order Value
                        </label>
                        <p />
                        <Form.Field
                            className={inputClassNames}
                            component={TextInput}
                            iconName={FLAG_ICONS[data[inputNames.CX_TO].value]}
                            iconSize="small"
                            name={inputNames.CX_TO_QUANTITY}
                            placeholder="0"
                            size="small"
                            type="number"
                        />
                    </Grid>
                </div>

                <Form.SubmitTrigger
                    render={({submit}) => (
                        <Button
                            className={buttonClassNames}
                            variant="primary"
                            size="large"
                            text="Place Order"
                            onClick={submit}
                        />
                    )}
                />
            </Form>
        )
    }
}

CurrencyExchangeForm.propTypes = {
    portfolio: PropTypes.shape({
        id: PropTypes.string,
        holdings: PropTypes.object,
    }).isRequired,
    currencies: PropTypes.object,
    data: PropTypes.shape({
        [inputNames.CX_FROM]: PropTypes.object,
        [inputNames.CX_TO]: PropTypes.object,
        [inputNames.CX_TO_QUANTITY]: PropTypes.string,
        [inputNames.CX_FROM_QUANTITY]: PropTypes.string,
    }),
    error: PropTypes.shape({
        [inputNames.CX_FROM]: PropTypes.string,
        [inputNames.CX_TO]: PropTypes.string,
        [inputNames.CX_TO_QUANTITY]: PropTypes.string,
        [inputNames.CX_FROM_QUANTITY]: PropTypes.string,
    }),
    exchangeCurrency: PropTypes.func,
    getAllCurrencies: PropTypes.func,
    getCurrencies: PropTypes.func,
    rate: PropTypes.number,
    validateOnUpdate: PropTypes.bool,
    onChange: PropTypes.func,
    onError: PropTypes.func,
    onSubmit: PropTypes.func,
}

CurrencyExchangeForm.defaultProps = {
    data: {...CurrencyExchangeForm.defaultState.data},
    error: {...CurrencyExchangeForm.defaultState.error},
    validateOnUpdate: true,
}

export default CurrencyExchangeForm
