"""
generate portfolio factsheet report using PortfolioData object
PortfolioData object can be generated by a backtester or actucal strategy data
with comparison to 1-2 cash benchmarks
PortfolioData can contain either simulated or actual portfolio data
"""
# packages
import pandas as pd
import matplotlib.pyplot as plt
from typing import Tuple, Optional

# qis
import qis
from qis import TimePeriod, PerfParams, BenchmarkReturnsQuantileRegimeSpecs

# portfolio
from qis.portfolio.portfolio_data import PortfolioData
from qis.portfolio.reports.config import PERF_PARAMS, REGIME_PARAMS


def generate_strategy_factsheet(portfolio_data: PortfolioData,
                                benchmark_prices: pd.DataFrame,
                                time_period: TimePeriod = None,
                                perf_params: PerfParams = PERF_PARAMS,
                                regime_params: BenchmarkReturnsQuantileRegimeSpecs = REGIME_PARAMS,
                                regime_benchmark: str = None,  # default is set to benchmark_prices.columns[0]
                                weight_freq: Optional[str] = 'B', #'W-WED',
                                figsize: Tuple[float, float] = (8.3, 11.7),  # A4 for portrait
                                **kwargs
                                ) -> plt.Figure:
    # align
    benchmark_prices = benchmark_prices.reindex(index=portfolio_data.nav.index, method='ffill')
    if regime_benchmark is None:
        regime_benchmark = benchmark_prices.columns[0]

    fig = plt.figure(figsize=figsize, constrained_layout=True)
    gs = fig.add_gridspec(nrows=14, ncols=4, wspace=0.0, hspace=0.0)

    plot_kwargs = dict(fontsize=5,
                       linewidth=0.5,
                       digits_to_show=1, sharpe_digits=2,
                       weight='normal',
                       markersize=1,
                       framealpha=0.75)
    kwargs = qis.update_kwargs(kwargs, plot_kwargs)
    fig.suptitle(f"{portfolio_data.nav.name} portfolio factsheet",
                 fontweight="bold", fontsize=8, color='blue')

    # prices
    joint_prices = pd.concat([portfolio_data.get_portfolio_nav(time_period=time_period),
                              benchmark_prices], axis=1).dropna()
    pivot_prices = joint_prices[regime_benchmark]
    ax = fig.add_subplot(gs[0:2, :2])
    qis.plot_prices(prices=joint_prices,
                    perf_params=perf_params,
                    title='Performance',
                    ax=ax,
                    **kwargs)
    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # dd
    ax = fig.add_subplot(gs[2:4, :2])
    qis.plot_rolling_drawdowns(prices=joint_prices,
                               title='Running Drawdowns',
                               dd_legend_type=qis.DdLegendType.SIMPLE,
                               ax=ax, **kwargs)
    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # exposures
    if len(portfolio_data.weights.columns) > 10:  # more than 10 use grouped exposures
        exposures = portfolio_data.get_exposures(is_grouped=True, time_period=time_period)
    else:
        exposures = portfolio_data.get_exposures(is_grouped=False, time_period=time_period)
    ax = fig.add_subplot(gs[4:6, :2])
    if weight_freq is not None:
        exposures = exposures.resample(weight_freq).last()
    qis.plot_stack(df=exposures,
                   add_mean_levels=False,
                   use_bar_plot=True,
                   baseline='zero',
                   title='Exposures',
                   legend_stats=qis.LegendStats.AVG_LAST,
                   var_format='{:.1%}',
                   ax=ax,
                   **qis.update_kwargs(kwargs, dict(bbox_to_anchor=(0.5, 1.05), ncol=2)))
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # turnover
    ax = fig.add_subplot(gs[6:8, :2])
    turnover = portfolio_data.get_turnover(time_period=time_period, roll_period=260)

    qis.plot_time_series(df=turnover,
                         var_format='{:,.2%}',
                         # y_limits=(0.0, None),
                         legend_stats=qis.LegendStats.AVG_LAST,
                         title='1y rolling average Turnover',
                         ax=ax,
                         **kwargs)

    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # benchmark betas
    ax = fig.add_subplot(gs[8:10, :2])
    factor_exposures = portfolio_data.compute_portfolio_benchmark_betas(benchmark_prices=benchmark_prices,
                                                                        time_period=time_period)
    qis.plot_time_series(df=factor_exposures,
                         var_format='{:,.2f}',
                         legend_stats=qis.LegendStats.AVG_LAST,
                         title='Portfolio Benchmark betas',
                         ax=ax,
                         **kwargs)
    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # attribution
    ax = fig.add_subplot(gs[10:12, :2])
    factor_attribution = portfolio_data.compute_portfolio_benchmark_attribution(benchmark_prices=benchmark_prices,
                                                                                time_period=time_period)
    qis.plot_time_series(df=factor_attribution,
                         var_format='{:,.0%}',
                         legend_stats=qis.LegendStats.LAST,
                         title='Portfolio Cumulative return attribution to benchmark betas',
                         ax=ax,
                         **kwargs)
    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # constituents
    ax = fig.add_subplot(gs[12:, :2])
    num_investable_instruments = portfolio_data.get_num_investable_instruments(time_period=time_period)
    qis.plot_time_series(df=num_investable_instruments,
                         var_format='{:,.0f}',
                         legend_stats=qis.LegendStats.FIRST_AVG_LAST,
                         title='Number of investable instruments',
                         ax=ax,
                         **kwargs)
    qis.add_bnb_regime_shadows(ax=ax, pivot_prices=pivot_prices, regime_params=regime_params)
    qis.set_spines(ax=ax, bottom_spine=False, left_spine=False)

    # ra perf table
    ax = fig.add_subplot(gs[0, 2:])
    portfolio_data.plot_ra_perf_table(ax=ax,
                                      benchmark_price=benchmark_prices[regime_benchmark],
                                      time_period=time_period,
                                      perf_params=perf_params,
                                      **qis.update_kwargs(kwargs, dict(fontsize=4)))
    ax = fig.add_subplot(gs[1, 2:])
    # change regression to weekly
    portfolio_data.plot_ra_perf_table(ax=ax,
                                      benchmark_price=benchmark_prices[regime_benchmark],
                                      time_period=qis.get_time_period_shifted_by_years(time_period=time_period),
                                      perf_params=perf_params,
                                      **qis.update_kwargs(kwargs, dict(fontsize=4, alpha_an_factor=52, freq_reg='W-WED')))

    # heatmap
    ax = fig.add_subplot(gs[2:4, 2:])
    portfolio_data.plot_monthly_returns_heatmap(ax=ax,
                                                time_period=time_period,
                                                title='Monthly Returns',
                                                **qis.update_kwargs(kwargs, dict(fontsize=4, date_format='%Y')))

    # periodic returns
    ax = fig.add_subplot(gs[4:6, 2:])
    local_kwargs = qis.update_kwargs(kwargs=kwargs,
                                     new_kwargs=dict(fontsize=4, square=False, x_rotation=90, transpose=True))
    portfolio_data.plot_periodic_returns(ax=ax,
                                         benchmark_prices=benchmark_prices,
                                         time_period=time_period,
                                         **local_kwargs)

    # perf contributors
    ax = fig.add_subplot(gs[6:8, 2])
    portfolio_data.plot_contributors(ax=ax,
                                     time_period=time_period,
                                     title=f"Performance Contributors {time_period.to_str()}",
                                     **kwargs)

    ax = fig.add_subplot(gs[6:8, 3])
    time_period_1y = qis.get_time_period_shifted_by_years(time_period=time_period)
    portfolio_data.plot_contributors(ax=ax,
                                     time_period=time_period_1y,
                                     title=f"Performance Contributors {time_period_1y.to_str()}",
                                     **kwargs)

    # regime data
    ax = fig.add_subplot(gs[8:10, 2:])
    portfolio_data.plot_regime_data(ax=ax,
                                    benchmark_price=benchmark_prices.iloc[:, 0],
                                    time_period=time_period,
                                    perf_params=perf_params,
                                    regime_params=regime_params,
                                    **kwargs)

    # vol regime data
    """
    ax = fig.add_subplot(gs[10:12, 2:])
    portfolio_data.plot_vol_regimes(ax=ax,
                                    benchmark_price=benchmark_prices.iloc[:, 0],
                                    time_period=time_period,
                                    perf_params=perf_params,
                                    regime_params=regime_params,
                                    **kwargs)
    """
    """
    # returns scatter
    ax = fig.add_subplot(gs[10:12, 2:])
    portfolio_data.plot_returns_scatter(ax=ax,
                                        benchmark_price=benchmark_prices.iloc[:, 0],
                                        time_period=time_period,
                                        freq=perf_params.freq_reg,
                                        **kwargs)

    if len(benchmark_prices.columns) > 1:
        ax = fig.add_subplot(gs[12:, 2:])
        portfolio_data.plot_returns_scatter(ax=ax,
                                            benchmark_price=benchmark_prices.iloc[:, 1],
                                            time_period=time_period,
                                            **kwargs)
    """
    local_kwargs = qis.update_kwargs(kwargs=kwargs, new_kwargs=dict(legend_loc=None))
    portfolio_data.plot_performance_attribution(time_period=time_period,
                                                attribution_metric=qis.AttributionMetric.PNL,
                                                ax=fig.add_subplot(gs[10:12, 2:]),
                                                **local_kwargs)

    portfolio_data.plot_performance_attribution(time_period=time_period,
                                                attribution_metric=qis.AttributionMetric.PNL_RISK,
                                                ax=fig.add_subplot(gs[12:, 2:]),
                                                **local_kwargs)

    return fig
