import React, {lazy, Suspense} from 'react'
import PropTypes from 'prop-types'

const Chart = lazy(() => import('react-uikit/chart'))
import SelectInput from 'react-uikit/select-input'
import {BENCHMARK_OPTIONS} from 'store/config'
import BEMModule from 'utils/bem'
import styles from './styles.scss'
import metricIds from 'global/constants/metric-ids'

const bem = new BEMModule(styles)

class PortfolioBenchmarkComparison extends React.PureComponent {
    static benchmarkOptions = [...BENCHMARK_OPTIONS]

    static getBenchmarkOption = (benchmarkId) => {
        return PortfolioBenchmarkComparison.benchmarkOptions.find(
            ({value}) => value === benchmarkId
        )
    }

    state = {
        selectedBenchmark:
            PortfolioBenchmarkComparison.getBenchmarkOption(
                this.props.portfolio.benchmarkId
            ) || PortfolioBenchmarkComparison.benchmarkOptions[0],
        benchmarkData: {id: null, x: [], y: []},
        portfolioData: {id: null, x: [], y: []},
    }

    get data() {
        return [this.state.portfolioData, this.state.benchmarkData]
    }

    componentDidMount() {
        const {getBenchmark, getTimeseries, portfolio} = this.props

        const {selectedBenchmark} = this.state

        const {
            courseSection,
            createdAt,
            id: portfolioId,
            isCoursePortfolio,
        } = portfolio

        // If the user is a part of a course, only fetch the benchmark values
        // during the duration of the course otherwise, fetch from the creation
        // date of the portfolio
        if (isCoursePortfolio) {
            getBenchmark({
                benchmarkId: selectedBenchmark.value,
                startDate: courseSection?.startDate,
                endDate: courseSection?.endDate,
            })
        } else {
            getBenchmark({
                benchmarkId: selectedBenchmark.value,
                startDate: createdAt,
            })
        }

        getTimeseries({portfolioId, timeseriesType: metricIds.MARKET_VALUE})
    }

    componentDidUpdate(prevProps, prevState) {
        const {selectedBenchmark: prevSelectedBenchmark} = prevState
        const {selectedBenchmark, benchmarkData, portfolioData} = this.state

        const {portfolio: prevPortfolio} = prevProps
        const {
            benchmarks,
            getBenchmark,
            getTimeseries,
            portfolio,
            portfolio: {
                courseSection,
                createdAt,
                id: portfolioId,
                name: portfolioName,
            },
            timeseries,
        } = this.props

        const benchmarkId = selectedBenchmark.value
        const isSamePortfolio =
            portfolioId === prevPortfolio.id &&
            portfolio.holdings.length === prevPortfolio.holdings.length
        const isSameBenchmark =
            selectedBenchmark.value === prevSelectedBenchmark.value

        // If the portfolio  or the benchmark has changed, we need to fetch &
        // reformat our historical data.
        if (!isSamePortfolio || !isSameBenchmark) {
            // Benchmark needs to be refetched
            const payload = {
                benchmarkId,
                startDate: courseSection?.startDate || createdAt,
                endDate: courseSection?.endDate,
            }

            getBenchmark(payload)

            // Portfolio timeseries only needs to be fetched if portfolio ID has changed
            !isSamePortfolio &&
                getTimeseries({
                    portfolioId,
                    timeseriesType: metricIds.MARKET_VALUE,
                })

            // While the timeseries are being fetched, we need to invalidate the
            // irrelevant timeseries data.
            this.setState({
                portfolioData: isSamePortfolio
                    ? portfolioData
                    : {id: null, x: [], y: []},
                benchmarkData: {id: null, x: [], y: []},
            })
            return
        }

        // Check for any invalid timeseries, and see if we can resolve that
        const isPortfolioDataValid = Boolean(portfolioData.id)
        const isBenchmarkDataValid = Boolean(benchmarkData.id)

        // If our current timeseries data is up-to-date, no need to do anything.
        if (isPortfolioDataValid && isBenchmarkDataValid) {
            return
        }

        // Update our chart data if we have the data to resolve it.
        const benchmark = benchmarks[benchmarkId]
        const canResolveBenchmark =
            !isBenchmarkDataValid &&
            benchmark &&
            !benchmark?.isLoading &&
            !benchmark?.error
        const canResolvePortfolio =
            !isPortfolioDataValid &&
            timeseries &&
            !timeseries?.isLoading &&
            !timeseries?.error

        if (!canResolvePortfolio && !canResolveBenchmark) {
            return
        }

        const nextBenchmarkData = canResolveBenchmark
            ? this.formatData(benchmark, benchmarkId, selectedBenchmark.label)
            : benchmarkData

        const nextPortfolioData = canResolvePortfolio
            ? this.formatData(timeseries, portfolioId, portfolioName)
            : portfolioData

        this.setState({
            benchmarkData: nextBenchmarkData,
            portfolioData: nextPortfolioData,
        })
    }

    formatData = ({ts, dailyPercentChange}, id, name) => {
        return {
            id,
            name,
            x: ts.map((date) => date.split('T')[0]),
            y: dailyPercentChange.map((v) => `${v.toFixed(2)}%`),
        }
    }

    setBenchmark = (option) => this.setState({selectedBenchmark: option})

    render() {
        const classes = bem.classNames('c-portfolio-benchmark-comparison')
        const containerClasses = bem.classNames(
            'c-portfolio-benchmark-comparison__heading-container'
        )
        const headingClasses = bem.classNames(
            'c-portfolio-benchmark-comparison__heading'
        )
        const inputClasses = bem.classNames(
            'c-portfolio-benchmark-comparison__input'
        )
        const graphClassNames = bem.classNames(
            'c-portfolio-benchmark-comparison__graph'
        )
        const isBlank = this.state.portfolioData.y.length < 2
        const hasCourseBenchmark = Boolean(this.props.portfolio.benchmarkId)

        return (
            <section className={classes}>
                <div className={containerClasses}>
                    <h3 className={headingClasses}>Benchmark Comparison</h3>
                    <SelectInput
                        className={inputClasses}
                        disabled={hasCourseBenchmark}
                        options={PortfolioBenchmarkComparison.benchmarkOptions}
                        size="small"
                        value={this.state.selectedBenchmark}
                        onChange={this.setBenchmark}
                    />
                </div>
                <Suspense fallback={null}>
                    <Chart
                        annotation="DAILY CHANGE  (%)"
                        className={graphClassNames}
                        config={{displayModeBar: false}}
                        data={this.data}
                        layout={{
                            showlegend: true,
                            legend: {
                                xanchor: 'right',
                                yanchor: 'bottom',
                                orientation: 'h',
                                x: 1,
                                y: 1,
                            },
                        }}
                        isBlank={isBlank}
                        type="scatter"
                    />
                </Suspense>
            </section>
        )
    }
}

PortfolioBenchmarkComparison.propTypes = {
    benchmarks: PropTypes.object,
    getBenchmark: PropTypes.func,
    getTimeseries: PropTypes.func,
    portfolio: PropTypes.object,
    timeseries: PropTypes.object,
}

export default PortfolioBenchmarkComparison
