import React from 'react'
import PropTypes from 'prop-types'
import moment from 'moment'
import isEqual from 'lodash.isequal'
import Debug from 'common-fe/utils/debug'
import SchoolClient from 'common-fe/clients/school'
import inputNames from 'common-fe/constants/input-names'
import FormError, {reason} from 'common-fe/utils/form-error'
import {validateInput} from 'common-fe/utils/validation'
import {sparsifyObj} from 'common-fe/utils'
import content from './content'
import {
    benchmarkOptions,
    fundingOptions,
    getSumOfRankingWeights,
} from './helpers'

import Button from 'react-uikit/button'
import Form from 'react-uikit/form'
import CourseSettings from './partials/course-settings'
import ChallengeCustomization from './partials/challenge-customization'

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

class CourseCreationForm extends React.Component {
    static formName = 'class-creation'

    static defaultState = {
        data: {
            [inputNames.COURSE_BENCHMARK]: benchmarkOptions[0],
            [inputNames.COURSE_CHALLENGE_TYPE]: null,
            [inputNames.COURSE_CODE]: '',
            [inputNames.COURSE_DESCRIPTION]: '',
            [inputNames.COURSE_END_DATE]: [],
            [inputNames.COURSE_ENGAGEMENT_MAX_CASH_PERCENTAGE]: '20',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_HOLDINGS]: '15',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_RATIONALES]: '5',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES]: '30',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_BONDS]: '5',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_EQUITIES]: '25',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_OPTIONS]: '10',
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_SHORTS]: '5',
            [inputNames.COURSE_INSTITUTION]: {label: '', value: ''},
            [inputNames.COURSE_MESSAGE]: '',
            [inputNames.COURSE_NAME]: '',
            [inputNames.COURSE_START_DATE]: [],
            [inputNames.COURSE_STARTING_VALUE]: fundingOptions[2],
            [inputNames.COURSE_ENGAGEMENT_IS_HOLDINGS]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_MAX_CASH_PERCENTAGE]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_RATIONALES]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_TRADES]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_TRADES_BONDS]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_TRADES_EQUITIES]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_TRADES_OPTIONS]: false,
            [inputNames.COURSE_ENGAGEMENT_IS_TRADES_SHORTS]: true,
            [inputNames.COURSE_ENGAGEMENT_IS_TRACKING]: true,
            [inputNames.IS_TEAM_PORTFOLIOS]: false,
            [inputNames.RANKING_WEIGHT_DIVERSIFICATION]: 0,
            [inputNames.RANKING_WEIGHT_ENGAGEMENT]: 0,
            [inputNames.RANKING_WEIGHT_RETURN]: 0,
            [inputNames.RANKING_WEIGHT_SHARPE_RATIO]: 0,
            [inputNames.RANKING_WEIGHT_VOLATILITY]: 0,
            [inputNames.RESTRICT_DAY_TRADING]: false,
            [inputNames.IS_TEAM_PORTFOLIOS]: false,
        },
    }

    static getFlags = (type) => {
        const flags = {
            holdings: [
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_HOLDINGS,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_HOLDINGS,
                },
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_MAX_CASH_PERCENTAGE,
                    key: inputNames.COURSE_ENGAGEMENT_MAX_CASH_PERCENTAGE,
                },
            ],
            rationales: [
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_RATIONALES,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_RATIONALES,
                },
            ],
            totalTrades: [
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_TRADES,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES,
                },
            ],
            // Trades by security type
            trades: [
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_TRADES_BONDS,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_BONDS,
                },
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_TRADES_EQUITIES,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_EQUITIES,
                },
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_TRADES_OPTIONS,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_OPTIONS,
                },
            ],
            // Trades by action type
            tradesAction: [
                {
                    flag: inputNames.COURSE_ENGAGEMENT_IS_TRADES_SHORTS,
                    key: inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_SHORTS,
                },
            ],
        }

        // return a subsection or all of them
        return flags[type] || Object.values(flags).flatten()
    }

    static pages = [CourseSettings, ChallengeCustomization]

    form = React.createRef()

    state = {
        currentPageIndex: 0,
        isFormValid: false,
    }

    componentDidMount() {
        const {schoolId} = this.props.user

        if (schoolId) {
            return SchoolClient.getSchool({schoolId})
                .then((school) => {
                    this.onChange({
                        name: inputNames.COURSE_INSTITUTION,
                        value: {label: school.name, value: schoolId},
                    })
                })
                .catch((err) =>
                    Debug.log(
                        `Could not retrieve school with id ${schoolId} - error: ${err}`
                    )
                )
        }

        return null
    }

    componentDidUpdate({data: previousData}) {
        const {data} = this.props

        if (isEqual(data, previousData)) {
            return false
        }

        // Check to see if the form is missing values that are needed to submit it
        const isInvalid = Object.keys(
            CourseCreationForm.defaultState.data
        ).some((key) => {
            // Ignore course challenge type
            if (key === inputNames.COURSE_CHALLENGE_TYPE) {
                return false
            }

            const value = data[key]

            // If the value is an array, check if it has any items in it
            if (Array.isArray(value) && value.length) {
                return false
            }
            // If the value is an object, check the value property to see if it has a value
            if (value.hasOwnProperty('value') && value.value) {
                return false
            }
            // If the value is of boolean or number type it has a value
            if (typeof value === 'boolean' || typeof value === 'number') {
                return false
            }
            // Otherwise if the item is falsy, the form is missing a value it needs
            return Array.isArray(value) ? true : !value
        })

        this.setState({isFormValid: !isInvalid})

        return true
    }

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

    getTotalMinTrades({name, value}) {
        const isAnyFlags = this.isAnyTradeFlagEnabled({name, value})
        if (!isAnyFlags) {
            return 0
        }

        return CourseCreationForm.getFlags('trades').reduce((sum, input) => {
            // if a value was supplied, use it instead of the controlled value
            const inputValue =
                input.key === name
                    ? parseInt(value)
                    : parseInt(this.props.data[input.key])
            const isInputEnabled =
                name === input.flag ? value : this.props.data[input.flag]

            return sum + (isInputEnabled && inputValue ? inputValue : 0)
        }, 0)
    }

    isAnyTradeFlagEnabled({name, value}) {
        return CourseCreationForm.getFlags('trades').some((input) => {
            // if the flag value is given use it instead of the controlled value
            return name === input.flag ? value : this.props.data[input.flag]
        })
    }

    validate = ({name, value, isRequired}) => {
        const {data, valid} = this.props
        let error = validateInput({name, value, isRequired})

        switch (name) {
            case inputNames.COURSE_INSTITUTION:
                error = validateInput({
                    name,
                    value: value.value,
                    isRequired: true,
                })
                break

            case inputNames.COURSE_END_DATE: {
                const endDate = data[inputNames.COURSE_END_DATE]
                const startDate = data[inputNames.COURSE_START_DATE]

                if (
                    endDate.length &&
                    startDate.length &&
                    moment(endDate[0]).isSameOrBefore(startDate[0])
                ) {
                    error = reason.END_DATE_BEFORE_START_ERR
                }

                break
            }

            case inputNames.RANKING_WEIGHT_DIVERSIFICATION:
            case inputNames.RANKING_WEIGHT_ENGAGEMENT:
            case inputNames.RANKING_WEIGHT_RETURN:
            case inputNames.RANKING_WEIGHT_SHARPE_RATIO:
            case inputNames.RANKING_WEIGHT_VOLATILITY: {
                const sum = getSumOfRankingWeights({data})

                if (sum !== 100) {
                    error = 'Sum of Ranking Weights must equal 100.'
                }
            }
        }

        this.props.onValid(valid)
        return error
    }

    goToNextStep = () => {
        this.setState(({currentPageIndex: previousIndex}) => ({
            currentPageIndex: previousIndex + 1,
        }))
    }

    goToPreviousStep = () => {
        this.setState(({currentPageIndex: previousIndex}) => ({
            currentPageIndex: previousIndex - 1,
        }))
    }

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

        // Change challenge type to custom if one of the presets is changed
        const nonCustomSettings = [
            inputNames.COURSE_CHALLENGE_TYPE,
            inputNames.COURSE_CODE,
            inputNames.COURSE_DESCRIPTION,
            inputNames.COURSE_END_DATE,
            inputNames.COURSE_INSTITUTION,
            inputNames.COURSE_MESSAGE,
            inputNames.COURSE_NAME,
            inputNames.COURSE_START_DATE,
        ]
        const isCustomSetting = !nonCustomSettings.includes(name)
        const challengeTypeData = isCustomSetting
            ? {[inputNames.COURSE_CHALLENGE_TYPE]: {label: 'Custom'}}
            : {}
        const formData = {...challengeTypeData, [name]: value}

        switch (name) {
            case inputNames.COURSE_CHALLENGE_TYPE: {
                const presetFormData =
                    content.challengeTypes[value.value].formData

                onChange &&
                    onChange({
                        ...data,
                        ...presetFormData,
                        ...formData,
                    })

                if (value?.label === 'Custom') {
                    this.goToNextStep()
                }

                return
            }

            case inputNames.COURSE_ENGAGEMENT_IS_TRADES_OPTIONS:
            case inputNames.COURSE_ENGAGEMENT_IS_TRADES_BONDS:
            case inputNames.COURSE_ENGAGEMENT_IS_TRADES_EQUITIES:
            case inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_BONDS:
            case inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_EQUITIES:
            case inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_OPTIONS: {
                const newMinTrades = this.getTotalMinTrades({name, value})

                onChange &&
                    onChange({
                        ...data,
                        ...formData,
                        [inputNames.COURSE_ENGAGEMENT_IS_TRADES]: !!newMinTrades,
                        [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES]: newMinTrades,
                    })
                return
            }

            case inputNames.RANKING_WEIGHT_DIVERSIFICATION:
            case inputNames.RANKING_WEIGHT_ENGAGEMENT:
            case inputNames.RANKING_WEIGHT_RETURN:
            case inputNames.RANKING_WEIGHT_SHARPE_RATIO:
            case inputNames.RANKING_WEIGHT_VOLATILITY: {
                this.validate({name, value})
            }

            // eslint-disable-line no-fallthrough
            default:
                onChange && onChange({...data, ...formData})
                return
        }
    }

    onUpdate = ({name, value}) => {
        switch (name) {
            case inputNames.COURSE_INSTITUTION:
                return

            default:
                this.onChange({name, value})
                return
        }
    }

    createCourse = (data) => {
        const {createCourse, showSuccess, onError, onSubmit} = this.props

        if (!createCourse) {
            return
        }

        const {
            [inputNames.COURSE_BENCHMARK]: {value: courseBenchmark},
            [inputNames.COURSE_CODE]: courseCode,
            [inputNames.COURSE_INSTITUTION]: {value: institution},
            [inputNames.COURSE_DESCRIPTION]: courseDescription,
            [inputNames.COURSE_END_DATE]: [endDate],
            [inputNames.COURSE_ENGAGEMENT_MAX_CASH_PERCENTAGE]: maxCashPercentage,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_HOLDINGS]: minHoldings,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_RATIONALES]: minRationales,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_BONDS]: minBondTrades,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_EQUITIES]: minEquityTrades,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_OPTIONS]: minOptionsTrades,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_SHORTS]: minShortTrades,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES]: minTotalTrades,
            [inputNames.COURSE_MESSAGE]: courseMessage,
            [inputNames.COURSE_NAME]: courseName,
            [inputNames.COURSE_START_DATE]: [startDate],
            [inputNames.COURSE_STARTING_VALUE]: {value: startingValue},
            [inputNames.IS_TEAM_PORTFOLIOS]: isTeamPortfolios,
            [inputNames.RANKING_WEIGHT_DIVERSIFICATION]: diversification,
            [inputNames.RANKING_WEIGHT_ENGAGEMENT]: engagement,
            [inputNames.RANKING_WEIGHT_RETURN]: roi,
            [inputNames.RANKING_WEIGHT_SHARPE_RATIO]: sharpe,
            [inputNames.RANKING_WEIGHT_VOLATILITY]: volatility,
            [inputNames.RESTRICT_DAY_TRADING]: restrictDayTrading,
        } = data

        const payload = sparsifyObj({
            [inputNames.COURSE_BENCHMARK]: courseBenchmark || null,
            [inputNames.COURSE_CODE]: courseCode,
            [inputNames.COURSE_INSTITUTION]: institution,
            [inputNames.COURSE_DESCRIPTION]: courseDescription,
            [inputNames.COURSE_END_DATE]: moment(endDate).format(),
            [inputNames.COURSE_ENGAGEMENT_MAX_CASH_PERCENTAGE]: maxCashPercentage
                ? Number(maxCashPercentage)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_HOLDINGS]: minHoldings
                ? Number(minHoldings)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_RATIONALES]: minRationales
                ? Number(minRationales)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_BONDS]: minBondTrades
                ? Number(minBondTrades)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_EQUITIES]: minEquityTrades
                ? Number(minEquityTrades)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_OPTIONS]: minOptionsTrades
                ? Number(minOptionsTrades)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_SHORTS]: minShortTrades
                ? Number(minShortTrades)
                : null,
            [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES]: minTotalTrades
                ? Number(minTotalTrades)
                : null,
            [inputNames.COURSE_MESSAGE]: courseMessage,
            [inputNames.COURSE_NAME]: courseName,
            [inputNames.COURSE_START_DATE]: moment(startDate).format(),
            [inputNames.COURSE_STARTING_VALUE]: startingValue,
            rankingWeights: {
                diversification: diversification / 100,
                engagement: engagement / 100,
                return: roi / 100,
                sharpe: sharpe / 100,
                volatility: volatility / 100,
            },
            [inputNames.IS_TEAM_PORTFOLIOS]: isTeamPortfolios,
            [inputNames.RESTRICT_DAY_TRADING]: restrictDayTrading,
            // [inputNames.COURSE_ENGAGEMENT_TOTAL_TRADES_FUTURES]: minFutureTrades,
            [inputNames.IS_TEAM_PORTFOLIOS]: isTeamPortfolios,
        })

        // Remove values if they aren't enabled
        CourseCreationForm.getFlags().forEach((input) => {
            if (
                !data[inputNames.COURSE_ENGAGEMENT_IS_TRACKING] ||
                !data[input.flag]
            ) {
                payload[input.key] = null
            }
        })

        createCourse(payload)
            .then((course) => {
                onSubmit?.(payload)
                showSuccess?.(course)
            })
            .catch((err) => {
                if (!err.inlineErrors || !err.inlineErrors.length || !onError) {
                    return
                }

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

                onError && onError(error)
            })
    }

    renderPage = (Component) => {
        const {data, error, user, valid} = this.props

        return (
            <Component
                data={data}
                error={error}
                goToNextStep={this.goToNextStep}
                goToPreviousStep={this.goToPreviousStep}
                onChange={this.onChange}
                user={user}
                valid={valid}
            />
        )
    }

    submitButton = ({disabled, submit}) => {
        const submitButtonClassNames = bem.classNames(
            'c-course-creation__button'
        )

        return (
            <Button
                className={submitButtonClassNames}
                disabled={disabled}
                variant="primary"
                text="Create"
                onClick={submit}
            />
        )
    }

    render() {
        const {data, error, onError} = this.props

        const {currentPageIndex, isFormValid} = this.state

        return (
            <Form
                data={data}
                error={error}
                name={CourseCreationForm.formName}
                ref={this.form}
                submitOnEnter={false}
                validate={this.validate}
                onChange={this.onChange}
                onError={onError}
                onUpdate={this.onUpdate}
                onSubmit={this.createCourse}
                validateOnUpdate={true}
            >
                {this.renderPage(CourseCreationForm.pages[currentPageIndex])}

                <Form.SubmitTrigger
                    disabled={!isFormValid}
                    render={this.submitButton}
                />
            </Form>
        )
    }
}

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

CourseCreationForm.propTypes = {
    createCourse: PropTypes.func,
    data: PropTypes.object,
    error: PropTypes.object,
    schoolSuggestions: PropTypes.object,
    showSuccess: PropTypes.func,
    updateSchoolSearch: PropTypes.func,
    user: PropTypes.shape({
        schoolId: PropTypes.string,
    }),
    valid: PropTypes.shape({}),
    onChange: PropTypes.func,
    onError: PropTypes.func,
    onSubmit: PropTypes.func,
    onValid: PropTypes.func,
}

export default CourseCreationForm
