from typing import Optional

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio


def _colorway() -> list:
    """
    Returns the list of colors from the plotly colorway
    :return: list of colors
    """
    return pio.templates[pio.templates.default].layout.colorway


def plot(
    fig: go.Figure,
    metric: pd.Series,
    cumulative: bool,
    label: str,
    volume: Optional[pd.Series] = None,
    benchmark: Optional[float] = None,
    percent_axis: Optional[bool] = True,
) -> go.Figure:
    """
    Generates the time series plot
    :param fig: plotly figure
    :param metric: series containing the data to plot
    :param cumulative: Whether to plot cumulative or rolling series
    :param label: label for the y axis
    :param volume: series containing the volume to plot
    :param benchmark: benchmark to plot
    :param percent_axis: whether to plot the y axis as percentage
    :return: a plotly figure
    """

    if cumulative:
        title = f"Cumulative {label}".title()
    else:
        title = f"Rolling {label}".title()

    fig.add_trace(
        go.Scatter(
            x=metric.index,
            y=metric.values,
            name=title,
            mode="lines+markers",
            marker={
                "color": "white",
                "size": 10,
                "line": {"color": _colorway()[0], "width": 3},
            },
            line={"color": _colorway()[0]},
        ),
        secondary_y=False,
    )

    if volume is not None:
        fig.add_trace(
            go.Scatter(
                x=volume.index,
                y=volume.values,
                name="Volume",
                mode="lines+markers",
                marker={
                    "color": "white",
                    "size": 10,
                    "line": {"color": _colorway()[1], "width": 2},
                    "symbol": "diamond",
                },
                line={"color": _colorway()[1]},
                opacity=0.7,
            ),
            secondary_y=True,
        )

    if benchmark is not None:
        fig.add_trace(
            go.Scatter(
                x=metric.index,
                y=np.ones(len(metric.index)) * benchmark,
                hoverinfo="skip",
                mode="lines",
                line={"color": "red", "dash": "dash"},
                name="Benchmark",
            ),
            secondary_y=False,
        )

    if percent_axis:
        fig.update_yaxes(tickformat="p", secondary_y=False)

    if volume is not None:
        fig.update_yaxes(title="Volume", secondary_y=True, showgrid=False, side="right")

    fig.update_yaxes(
        secondary_y=False,
        title=title,
        showgrid=False,
        side="left",
        title_font=dict(color=_colorway()[0]),
    )
    fig.update_xaxes(title="Date")
    fig.update_layout(showlegend=False)

    if volume is not None:
        fig.update_yaxes(title_font=dict(color=_colorway()[1]), secondary_y=True)

    fig.update_layout(hovermode="x", hoverlabel=dict(font=dict(color="white"), bgcolor=_colorway()[0]))

    return fig


def plot_by_primary_channel(
    fig: go.Figure,
    metric_dic: dict,
    cumulative: bool,
    label: str,
    benchmark: Optional[float] = None,
    percent_axis: Optional[bool] = True,
) -> go.Figure:
    """
    Generates the time series plot by primary channel
    :param fig: plotly figure
    :param metric_dic: dictionary containing the data to plot
    :param cumulative: Whether to plot cumulative or rolling series
    :param label: label for the y axis
    :param benchmark: benchmark to plot
    :param percent_axis: whether to plot the y axis as percentage
    :return: a plotly figure
    """

    if cumulative:
        title = f"Cumulative {label}".title()
    else:
        title = f"Rolling {label}".title()

    for i, (channel, metric) in enumerate(metric_dic.items()):
        fig.add_trace(
            go.Scatter(
                x=metric.index,
                y=metric.values,
                name=channel,
                mode="lines+markers",
                marker={
                    "color": "white",
                    "size": 10,
                    "line": {"color": _colorway()[i], "width": 3},
                },
                line={"color": _colorway()[i]},
            ),
            secondary_y=False,
        )

    all_indices = pd.concat([metric for (_, metric) in metric_dic.items()]).index
    idx = pd.Index([all_indices.min(), all_indices.max()])

    if benchmark is not None:
        fig.add_trace(
            go.Scatter(
                x=idx,
                y=np.ones(len(idx)) * benchmark,
                hoverinfo="skip",
                mode="lines",
                line={"color": "red", "dash": "dash"},
                name="Benchmark",
            ),
            secondary_y=False,
        )

    if percent_axis:
        fig.update_yaxes(tickformat="p", secondary_y=False)

    fig.update_yaxes(title=title, secondary_y=False, showgrid=False, side="left")
    fig.update_xaxes(title="Date")
    fig.update_layout(showlegend=True)

    return fig
