"""
ROE 因子。
"""

import pandas as pd

from factors.base import BaseFactor


class ROEFactor(BaseFactor):
    """
    ROE 因子。

    从财务数据提取 ROE 并映射到日线。

    需要 df 中包含 'roe' 列（由 FactorEngine 合并财务数据后传入），
    或将 financial_df 直接传入构造函数。
    """

    category = "fundamental"

    def __init__(self, financial_df: pd.DataFrame | None = None):
        """
        参数:
            financial_df: 财务数据 DataFrame，columns 含 'end_date', 'roe'。
                          None 时需在 df 参数中直接提供 roe 列。
        """
        self._financial_df = financial_df
        self.name = "roe"

    def calculate(self, df: pd.DataFrame) -> pd.Series:
        if "roe" in df.columns:
            return df["roe"].copy()

        if self._financial_df is None or self._financial_df.empty:
            return pd.Series(float("nan"), index=df.index)

        return self._map_financial_to_daily(
            df, self._financial_df, "roe"
        )

    @staticmethod
    def _map_financial_to_daily(
        daily_df: pd.DataFrame,
        fina_df: pd.DataFrame,
        column: str,
    ) -> pd.Series:
        """将财务数据（季度）映射到日线索引。"""
        fina = fina_df[["end_date", column]].dropna().copy()
        fina["end_date"] = fina["end_date"].astype(str)
        fina = fina.sort_values("end_date")

        result = pd.Series(float("nan"), index=daily_df.index)

        if fina.empty:
            return result

        dates = pd.to_datetime(daily_df.index, format="%Y%m%d", errors="coerce")
        fina_dates = pd.to_datetime(fina["end_date"], format="%Y%m%d", errors="coerce")

        for i, fina_date in enumerate(fina_dates):
            mask = dates >= fina_date
            if i + 1 < len(fina_dates):
                mask &= dates < fina_dates.iloc[i + 1]
            else:
                pass  # 最新一期覆盖所有后续日期
            result[mask] = fina[column].iloc[i]

        return result.astype("float64")


class ROETTMDeltaFactor(BaseFactor):
    """ROE 同比变化（当前 ROE - 去年同期 ROE）。"""

    category = "fundamental"

    def __init__(self, financial_df: pd.DataFrame | None = None):
        self._financial_df = financial_df
        self.name = "roe_delta"

    def calculate(self, df: pd.DataFrame) -> pd.Series:
        if self._financial_df is None or self._financial_df.empty:
            return pd.Series(float("nan"), index=df.index)

        fina = self._financial_df[["end_date", "roe"]].dropna().copy()
        fina["end_date"] = fina["end_date"].astype(str)
        fina["year"] = fina["end_date"].str[:4].astype(int)
        fina = fina.sort_values("end_date")

        # 按年分组计算 YoY 差值
        roe_delta = pd.Series(float("nan"), index=df.index)
        dates = pd.to_datetime(df.index, format="%Y%m%d", errors="coerce")

        for _, row in fina.iterrows():
            this_year = row["year"]
            prev_row = fina[fina["year"] == this_year - 1]
            if prev_row.empty:
                continue
            delta = row["roe"] - prev_row["roe"].iloc[-1]
            f_date = pd.to_datetime(row["end_date"], format="%Y%m%d")
            mask = dates >= f_date
            roe_delta[mask] = delta

        return roe_delta.astype("float64")
