"""训练器主类

整合数据处理、模型训练、预测的完整流程。

.. deprecated::
    此模块已废弃，将在后续版本中移除。
    请使用 src.training.trainer_v2.Trainer 替代。
    新 Trainer 提供更灵活的模块化设计，支持 FactorManager、
    DataPipeline、Task 策略等组件。
"""

import warnings
from typing import TYPE_CHECKING, List, Optional

import polars as pl

from src.training.components.base import BaseModel, BaseProcessor
from src.training.components.splitters import DateSplitter
from src.training.core.stock_pool_manager import StockPoolManager

if TYPE_CHECKING:
    from src.training.components.filters import BaseFilter


def _warn_deprecated():
    """发出废弃警告"""
    warnings.warn(
        "src.training.core.trainer.Trainer 已废弃，将在后续版本中移除。"
        "请使用 src.training.trainer_v2.Trainer 替代。",
        DeprecationWarning,
        stacklevel=3,
    )


class Trainer:
    """训练器主类

    整合数据处理、模型训练、预测的完整流程。

    关键设计：
    1. 因子先计算（全市场），再筛选股票池（每日独立）
    2. Processor 分阶段行为：训练集 fit_transform，测试集 transform
    3. 一次性训练，不滚动
    4. 支持模型持久化
    """

    def __init__(
        self,
        model: BaseModel,
        pool_manager: Optional[StockPoolManager] = None,
        processors: Optional[List[BaseProcessor]] = None,
        filters: Optional[List["BaseFilter"]] = None,
        splitter: Optional[DateSplitter] = None,
        target_col: str = "target",
        feature_cols: Optional[List[str]] = None,
        persist_model: bool = False,
        model_save_path: Optional[str] = None,
    ):
        """初始化训练器

        .. deprecated::
            请使用 src.training.trainer_v2.Trainer 替代。

        Args:
            model: 模型实例
            pool_manager: 股票池管理器，None 表示不筛选
            processors: 数据处理器列表
            filters: 数据过滤器列表（在股票池筛选之前执行）
            splitter: 数据划分器
            target_col: 目标变量列名
            feature_cols: 特征列名列表
            persist_model: 是否保存模型
            model_save_path: 模型保存路径
        """
        _warn_deprecated()
        self.model = model
        self.pool_manager = pool_manager
        self.processors = processors or []
        self.filters = filters or []
        self.splitter = splitter
        self.target_col = target_col
        self.feature_cols = feature_cols or []
        self.persist_model = persist_model
        self.model_save_path = model_save_path

        # 存储训练后的处理器
        self.fitted_processors: List[BaseProcessor] = []
        self.results: Optional[pl.DataFrame] = None

    def train(self, data: pl.DataFrame) -> "Trainer":
        """执行训练流程

        流程：
        1. 股票池每日筛选（如果配置了 pool_manager）
        2. 按日期划分训练集/测试集
        3. 训练集：processors fit_transform
        4. 训练模型
        5. 测试集：processors transform（使用训练集学到的参数）
        6. 预测
        7. 保存结果
        8. 持久化模型（如果启用）

        Args:
            data: 因子计算后的全市场数据
                  必须包含 ts_code 和 trade_date 列

        Returns:
            self (支持链式调用)
        """
        # 0. 数据过滤（在股票池筛选之前）
        if self.filters:
            print("[过滤] 应用数据过滤器...")
            for filter_ in self.filters:
                data = filter_.filter(data)

        # 1. 股票池筛选（每日独立）
        if self.pool_manager:
            print("[筛选] 每日独立筛选股票池...")
            data = self.pool_manager.filter_and_select_daily(data)

        # 2. 划分训练/验证/测试集（三分法）
        if self.splitter:
            print("[划分] 划分训练集、验证集和测试集...")
            train_data, val_data, test_data = self.splitter.split(data)
        else:
            # 没有划分器，全部作为训练集
            train_data = data
            val_data = data
            test_data = data

        # 3. 训练集：processors fit_transform
        if self.processors:
            print("[处理] 处理训练集...")
            for processor in self.processors:
                train_data = processor.fit_transform(train_data)
                self.fitted_processors.append(processor)

        # 4. 训练模型
        print("[训练] 训练模型...")
        if not self.feature_cols:
            raise ValueError("feature_cols 不能为空")

        X_train = train_data.select(self.feature_cols)
        y_train = train_data.select(self.target_col).to_series()
        self.model.fit(X_train, y_train)

        # 5. 测试集：processors transform
        if self.processors and test_data is not train_data:
            print("[处理] 处理测试集...")
            for processor in self.fitted_processors:
                test_data = processor.transform(test_data)

        # 6. 预测
        print("[预测] 生成预测...")
        X_test = test_data.select(self.feature_cols)
        predictions = self.model.predict(X_test)

        # 7. 保存结果
        self.results = test_data.with_columns([pl.Series("prediction", predictions)])

        # 8. 持久化模型
        if self.persist_model and self.model_save_path:
            print(f"[保存] 保存模型到 {self.model_save_path}...")
            self.save_model(self.model_save_path)

        return self

    def predict(self, data: pl.DataFrame) -> pl.DataFrame:
        """对新数据进行预测

        注意：新数据需要先经过股票池筛选，
        然后使用训练好的 processors 和 model 进行预测。

        Args:
            data: 输入数据

        Returns:
            包含预测列的数据
        """
        # 应用 processors
        for processor in self.fitted_processors:
            data = processor.transform(data)

        # 预测
        X = data.select(self.feature_cols)
        predictions = self.model.predict(X)

        return data.with_columns([pl.Series("prediction", predictions)])

    def get_results(self) -> Optional[pl.DataFrame]:
        """获取所有预测结果

        Returns:
            预测结果 DataFrame，包含原始列和 prediction 列
        """
        return self.results

    def save_results(self, path: str) -> None:
        """保存预测结果到文件

        Args:
            path: 保存路径（CSV 格式）
        """
        if self.results is not None:
            self.results.write_csv(path)

    def save_model(self, path: str) -> None:
        """保存模型

        Args:
            path: 模型保存路径
        """
        self.model.save(path)
