import abc
from typing import ClassVar, Self, Sequence

from bayesline.api._src._utils import docstrings_from_sync
from bayesline.api._src.equity.portfolioreport import (
    AsyncReportAccessorApi,
    ReportAccessorApi,
)
from bayesline.api._src.equity.report.scenarios import AnyScenarioSettings


class TypedReportAccessorApi(abc.ABC):
    """
    A base interface for typed report accessor APIs.

    It is meant to be extended by concrete report implementations, e.g.
    a `FactorCovarianceReportAccessorApi` which adds concrete functions that
    operate on the underyling low level report accessor and slice report data
    to specific representations, e.g. `def fcov` which returns the correct
    dataframe representation of the factor covariance matrix instead the raw
    report dataframe.
    """

    scenario_types: ClassVar[list[type[AnyScenarioSettings]]] = []

    @abc.abstractmethod
    def __init__(self, accessor: ReportAccessorApi): ...  # noqa: DOC101 DOC103

    @property
    @abc.abstractmethod
    def accessor(self) -> ReportAccessorApi:
        """
        The underlying report accessor API.

        Returns
        -------
        ReportAccessorApi
            The underlying report accessor API.
        """

    def with_scenario(
        self, scenario_settings: AnyScenarioSettings | Sequence[AnyScenarioSettings]
    ) -> Self:
        """Return a new accessor with the given scenario settings applied.

        Parameters
        ----------
        scenario_settings : AnyScenarioSettings | Sequence[AnyScenarioSettings]
            The scenario settings to apply.

        Returns
        -------
        Self
            A new accessor with the scenario settings applied.

        Raises
        ------
        NotImplementedError
            If this accessor does not support scenarios.
        """
        new_inner = self.accessor.with_scenario_settings(scenario_settings)
        return type(self)(new_inner)


@docstrings_from_sync
class AsyncTypedReportAccessorApi(abc.ABC):

    scenario_types: ClassVar[list[type[AnyScenarioSettings]]] = []

    @abc.abstractmethod
    def __init__(self, accessor: AsyncReportAccessorApi): ...

    @property
    @abc.abstractmethod
    def accessor(self) -> AsyncReportAccessorApi: ...  # noqa: D102

    async def with_scenario(  # noqa: D102
        self, scenario_settings: AnyScenarioSettings | Sequence[AnyScenarioSettings]
    ) -> Self:
        new_inner = await self.accessor.with_scenario_settings(scenario_settings)
        return type(self)(new_inner)
