import React from 'react'
import PropTypes from 'prop-types'
import FormError, {reason} from 'common-fe/utils/form-error'
import {getHoldingsBySecurityType, getHolding} from 'utils/portfolio'
import modalTypes from 'containers/modal-manager/modal-types'
import {renderWithCurrency, renderLargeNumber} from 'common-fe/utils/render'
import {validateInput} from 'common-fe/utils/validation'
import inputNames from 'common-fe/constants/input-names'
import optionTypes from 'common-fe/constants/option-types'
import {OPTION_EXERCISED} from 'common/constants/order-types'

import SubmitButton from './partials/submit-button'
import Form from 'react-uikit/form'
import Icon from 'react-uikit/icon'
import Layout from 'react-uikit/layout'
import TextInput from 'react-uikit/input-text'

import BEMModule from 'utils/bem'
import styles from './styles.scss'
const bem = new BEMModule(styles)

class OptionExerciseForm extends React.Component {
    // STATIC PROPERTIES =======================================================
    static formName = 'option-exercise'

    static defaultState = {
        data: {
            [inputNames.ORDER_QUANTITY]: '',
        },
        error: {},
    }

    static getDerivedStateFromProps(nextProps) {
        const {
            security: {id: optionId, type: optionSecurityType},
            portfolio: {holdings: securities},
            underlyingExchange,
            underlyingSecurity: {id: securityId, type: securityType},
        } = nextProps

        // Grab the option from our holdings
        const optionsHoldings = getHoldingsBySecurityType(
            {securities},
            optionSecurityType
        )
        const option = getHolding({
            holdings: optionsHoldings,
            securityId: optionId,
        })

        const securityHoldings = getHoldingsBySecurityType(
            {securities},
            securityType
        )
        const holding = getHolding({holdings: securityHoldings, securityId})

        return {
            option,
            holding,
            isMarketClosed: underlyingExchange
                ? underlyingExchange.isClosed
                : false,
        }
    }

    // LIFECYCLE ===============================================================

    // FORM ====================================================================
    state = {option: {}}

    componentDidMount() {
        // Need to fetch the individual security to get market the next open/close
        // of the market.
        this.props.getSecurity &&
            this.props.getSecurity({
                securityId: this.option.underlyingSecurityId,
            })
    }

    get initialState() {
        const defaultExerciseQuantity = this.option ? this.option.quantity : ''

        // This is necessary to pass up the exercise quantity to the modal and to
        // get the data back to the options portfolio info banner. Not the best sol'n
        // but we can think about how to improve this later.
        this.change({
            name: inputNames.ORDER_QUANTITY,
            value: defaultExerciseQuantity,
        })

        return {
            data: {
                ...OptionExerciseForm.defaultState.data,
                [inputNames.ORDER_QUANTITY]: defaultExerciseQuantity,
            },
            error: {...OptionExerciseForm.defaultState.error},
        }
    }

    getExerciseState = () => {
        // An option can only be exercised if the following are all true:
        // 1 The option is in a long position
        if (this.option.position === 'short') {
            return {
                canExercise: false,
                reason:
                    'You cannot exercise an option that is in short position',
            }
        }

        // 2 The underlying market is open
        if (this.state.isMarketClosed) {
            const {
                message: {status, time},
            } = this.props.underlyingExchange

            return {
                canExercise: false,
                reason: `The market is currently closed for the underlying security. ${status} ${time}.`,
            }
        }

        // 3 For a PUT contract, the underlying security exists in our holdings with enough quantity
        if (this.option.optionType === optionTypes.PUT.value) {
            const {
                data: {[inputNames.ORDER_QUANTITY]: exerciseQty},
                underlyingSecurity: {ticker: underlyingTicker},
            } = this.props

            const holdingQty = this.state.holding
                ? this.state.holding.quantity
                : 0
            const sellQty = exerciseQty * this.option.lotSize
            const hasSufficientQty = holdingQty >= sellQty

            return {
                canExercise: hasSufficientQty,
                reason: !hasSufficientQty
                    ? `A minimum of ${renderLargeNumber(
                          sellQty
                      )} ${underlyingTicker} shares in your holdings is required to exercise this option.`
                    : null,
            }
        }

        // 4 For a CALL contract, the underlying security must not be in a short position
        if (this.option.optionType === optionTypes.CALL.value) {
            const {
                underlyingSecurity: {ticker: underlyingTicker},
            } = this.props

            const isShortPosition = this.state.holding
                ? this.state.holding.position === 'short'
                : false

            return {
                canExercise: !isShortPosition,
                reason: isShortPosition
                    ? `You cannot exercise this option because you are currently in a short position of ${underlyingTicker}.`
                    : null,
            }
        }

        return {
            canExercise: true,
            reason: null,
        }
    }

    change = ({name, value}) => {
        const {onChange} = this.props
        const payload = {[name]: value}

        onChange && onChange(payload)
    }

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

        const payload = {[name]: value}

        switch (name) {
            case inputNames.ORDER_QUANTITY:
                payload[name] = Math.floor(value)
                break
        }

        onChange && onChange(payload)
    }

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

        if (error) {
            return error
        }

        switch (name) {
            case inputNames.ORDER_QUANTITY: {
                if (value > this.option.quantity) {
                    error = reason.INSUFFICIENT_OPTIONS_ERR
                }
                break
            }
        }

        return error
    }

    submit = ({[inputNames.ORDER_QUANTITY]: quantity}) => {
        const {
            exerciseOption,
            portfolio: {id: portfolioId},
            showModal,
            onError,
            onSubmit,
        } = this.props

        const payload = {
            portfolioId,
            quantity,
            securityId: this.option.id,
            orderType: OPTION_EXERCISED,
        }

        exerciseOption &&
            exerciseOption(payload)
                .then((order) => {
                    onSubmit &&
                        onSubmit(payload).then(() => {
                            showModal &&
                                showModal({
                                    type: modalTypes.MODAL_TRADE_RATIONALE,
                                    order,
                                })
                        })
                })
                .catch((err) => {
                    // TODO: account for new exercise option errors
                    if (
                        !err.inlineErrors ||
                        !err.inlineErrors.length ||
                        !onError
                    ) {
                        return
                    }

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

                    onError(error)
                })
    }

    // EXERCISE ================================================================

    /**
     *  Option: The contract that user has in their portfolio.
     *  @return  Object
     */

    get option() {
        return this.state.option
    }

    /**
     *  Amount Tradeable: User should be able to execise any number of contracts
     *  they own.
     *  @return  Number  e.g. 10
     */
    get amountExercisable() {
        return this.option ? this.option.quantity : 0
    }

    /**
     *  Estimated Order Value
     *  @return  Number  e.g. 22.04
     */
    get orderValue() {
        const {[inputNames.ORDER_QUANTITY]: orderQuantity} = this.props.data
        const quantity = isNaN(orderQuantity) ? 0 : orderQuantity
        const {strikePrice, lotSize} = this.props.security

        // Get estimated price based on security type and order action
        return isNaN(strikePrice) ? '' : quantity * strikePrice * lotSize
    }

    render() {
        const {
            data,
            error,
            onError,
            security: {currency, strikePrice, lotSize},
        } = this.props

        const optionExerciseFormButtonClasses = bem.classNames(
            'c-option-exercise__button'
        )
        const optionExerciseFormWrapperClasses = bem.classNames(
            'c-option-exercise__wrapper'
        )
        const optionExerciseFormLabelsClasses = bem.classNames(
            'c-option-exercise__labels'
        )
        const optionExerciseFormPriceValueClasses = bem.classNames(
            'c-option-exercise__price-value'
        )
        const optionExerciseFormResultValueClasses = bem.classNames(
            'c-option-exercise__result-value'
        )
        const optionExerciseFormHRuleClasses = bem.classNames(
            'c-option-exercise__h-rule'
        )
        const {canExercise, reason: exerciseTooltip} = this.getExerciseState()

        return (
            <Form
                data={data}
                error={error}
                name={OptionExerciseForm.formName}
                submitOnEnter
                validate={this.validate}
                onChange={this.change}
                onError={onError}
                onSubmit={this.submit}
                onUpdate={this.update}
            >
                <Layout.Grid className={optionExerciseFormWrapperClasses}>
                    <Layout.Flex
                        className={optionExerciseFormLabelsClasses}
                        align="start"
                        justify="end"
                    >
                        Contracts to Exercise
                    </Layout.Flex>
                    <div />
                    <Form.Field
                        max={this.amountExercisable}
                        maxLength={this.amountExercisable}
                        min={0}
                        name={inputNames.ORDER_QUANTITY}
                        placeholder="0"
                        render={TextInput}
                        required
                        showLength={false}
                        size="small"
                        textAlign="right"
                        type="number"
                        withCharCount
                    />

                    <Layout.Flex
                        className={optionExerciseFormLabelsClasses}
                        align="center"
                        justify="end"
                    >
                        Lot Size
                    </Layout.Flex>

                    <Layout.Flex align="center" justify="center">
                        <Icon name="close" style={{maxWidth: '0.8rem'}} />
                    </Layout.Flex>

                    <Layout.Flex
                        className={optionExerciseFormPriceValueClasses}
                        align="center"
                        justify="end"
                    >
                        {renderLargeNumber(lotSize)}
                    </Layout.Flex>

                    <Layout.Flex
                        className={optionExerciseFormLabelsClasses}
                        align="center"
                        justify="end"
                    >
                        Strike Price
                    </Layout.Flex>
                    <Layout.Flex align="center" justify="center">
                        <Icon name="close" style={{maxWidth: '0.8rem'}} />
                    </Layout.Flex>
                    <Layout.Flex
                        className={optionExerciseFormPriceValueClasses}
                        align="center"
                        justify="end"
                    >
                        {renderWithCurrency(strikePrice, currency, {
                            decimal: 2,
                        })}
                    </Layout.Flex>

                    <Layout.Flex align="center" style={{gridColumn: '1 / 4'}}>
                        <hr className={optionExerciseFormHRuleClasses} />
                    </Layout.Flex>

                    <Layout.Flex
                        className={optionExerciseFormLabelsClasses}
                        align="center"
                        justify="end"
                    >
                        Total
                    </Layout.Flex>
                    <div />
                    <Layout.Flex
                        className={optionExerciseFormResultValueClasses}
                        align="center"
                        justify="end"
                    >
                        {renderWithCurrency(this.orderValue, currency, {
                            largeNumber: true,
                            decimal: 2,
                        })}
                    </Layout.Flex>
                </Layout.Grid>

                <Form.SubmitTrigger
                    className={optionExerciseFormButtonClasses}
                    disabled={!canExercise}
                    tooltip={exerciseTooltip}
                    render={SubmitButton}
                />
            </Form>
        )
    }
}

OptionExerciseForm.propTypes = {
    portfolio: PropTypes.object.isRequired,
    security: PropTypes.object.isRequired,
    underlyingSecurity: PropTypes.object.isRequired,
    data: PropTypes.object,
    error: PropTypes.object,
    exerciseOption: PropTypes.func,
    getSecurity: PropTypes.func,
    showModal: PropTypes.func,
    underlyingExchange: PropTypes.object,
    onChange: PropTypes.func,
    onError: PropTypes.func,
    onSubmit: PropTypes.func,
}

OptionExerciseForm.defaultProps = {
    data: {...OptionExerciseForm.defaultState.data},
    error: {...OptionExerciseForm.defaultState.error},
    valid: {...OptionExerciseForm.defaultState.valid},
}

export default OptionExerciseForm
