import React from 'react'
import PropTypes from 'prop-types'
import CookieClient from 'common-fe/clients/cookie'
import hoistNonReactStatics from 'hoist-non-react-statics'
import PageNotFound from 'containers/pages/not-found'
import {
    PORTFOLIO_HOLDINGS,
    PORTFOLIO_PERFORMANCE,
} from 'global/constants/portfolio-tabs'
import routes from 'global/constants/routes'
import rankingPeriods from 'global/constants/ranking-periods'

const withPortfolioPage = (Component) => {
    class PortfolioPage extends React.Component {
        // ===============================
        //  STATIC METHODS & PROPERTIES
        // ===============================

        static getPortfolioId = (props) => {
            const {match, user} = props

            if (!match?.params?.portfolioId) {
                // If there is no portfolio ID specified in our URL,
                // check our cookies for the last portfolio visited or
                // use the user's last portfolio. Return null if neither exist.
                const lastViewedPortfolio = CookieClient.get.portfolioId()
                return lastViewedPortfolio
                    ? lastViewedPortfolio
                    : user.portfolios[user.portfolios.length - 1] || null
            }

            return decodeURIComponent(match.params.portfolioId)
        }

        static getDerivedStateFromProps(nextProps) {
            const portfolioId = PortfolioPage.getPortfolioId(nextProps)
            const {user} = nextProps

            // Currently we do not allow users to view peer portfolios
            // TODO: update handling once we enable that feature.
            if (!user.portfolios.includes(portfolioId)) {
                return {portfolioExists: false}
            }

            // We have portfolio data for the next portfolio being viewed.
            // Allow the render function to handle the rest.
            if (nextProps.portfolio) {
                return {portfolioExists: true}
            }

            // We don't have information on the portfolio being visited,
            // this would most likely mean that the user has visited a deleted
            // portfolio or indicated an incorrect ID in the url.
            return {
                portfolioExists: false,
            }
        }

        // ===============================
        //  INSTANCE METHODS & PROPERTIES
        // ===============================

        state = {portfolioExists: true}

        componentDidMount() {
            if (!this.portfolioId) {
                return
            }

            this.setPortfolio(this.portfolioId)
        }

        componentDidUpdate(prevProps, prevState) {
            // If the portfolio still doesn't exist, there is nothing extra to do.
            if (!prevState.portfolioExists && !this.state.portfolioExists) {
                return
            }

            const prevPortfolioId = PortfolioPage.getPortfolioId(prevProps)
            const currPortfolioId = this.portfolioId

            // If the previously loaded portfolio does not have the same ID as the
            // portfolio ID we are needing to load next, set the portfolio.
            if (prevPortfolioId !== currPortfolioId) {
                this.setPortfolio(currPortfolioId)
            }

            // Since we render Page Not Found when a portfolio does not exist,
            // we need to reset the page title if we update with a portfolio
            if (!prevState.portfolioExists && this.state.portfolioExists) {
                this.props.updateTitle &&
                    this.props.updateTitle({title: Component.pageTitle})
            }
        }

        setPortfolio = (portfolioId) => {
            this.props
                .setPortfolio?.({portfolioId}, {overrideLoader: true})
                .then(() =>
                    this.props.getPortfolioSharpeRatio?.(
                        {portfolioId, period: rankingPeriods.ALL_TIME},
                        {overrideLoader: true}
                    )
                )
                .catch(() => {
                    // Setting the portfolio failed because the portfolio
                    // does not exist
                    this.setState({portfolioExists: false})
                })
        }

        get portfolioId() {
            return PortfolioPage.getPortfolioId(this.props)
        }

        // Navigation ==========================================================
        get tabs() {
            const holdingsTab = {
                ...PORTFOLIO_HOLDINGS,
                route: `${routes.PORTFOLIO}/${encodeURIComponent(
                    this.portfolioId
                )}${routes.PORTFOLIO_HOLDINGS}`,
            }
            const performanceTab = {
                ...PORTFOLIO_PERFORMANCE,
                route: `${routes.PORTFOLIO}/${encodeURIComponent(
                    this.portfolioId
                )}${routes.PORTFOLIO_PERFORMANCE}`,
            }

            return [holdingsTab, performanceTab]
        }

        render() {
            if (!this.state.portfolioExists) {
                return <PageNotFound />
            }

            return <Component tabs={this.tabs} {...this.props} />
        }
    }

    PortfolioPage.displayName = `withPortfolioPage(${
        Component.displayName || Component.name
    })`

    PortfolioPage.propTypes = {
        getPortfolioSharpeRatio: PropTypes.func,
        match: PropTypes.shape({
            params: PropTypes.shape({
                portfolioId: PropTypes.string,
            }),
        }),
        portfolio: PropTypes.object,
        setPortfolio: PropTypes.func,
        updateTitle: PropTypes.func,
        user: PropTypes.shape({
            portfolios: PropTypes.array,
        }),
    }

    return hoistNonReactStatics(PortfolioPage, Component)
}

export default withPortfolioPage
