# src/analysis/result_analyzer.py

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from typing import List, Dict, Any, Optional

from src.indicators.base_indicators import Indicator

# 导入纯函数 (注意相对导入路径的变化)
from .analysis_utils import (
    calculate_metrics,
    plot_equity_and_drawdown_chart,
    plot_close_price_chart,
)

# 导入核心数据类 (注意相对导入路径的变化)
from ..core_data import PortfolioSnapshot, Trade, Bar


class ResultAnalyzer:
    """
    结果分析器：负责接收回测数据，并提供分析和可视化方法。
    """

    def __init__(
        self,
        portfolio_snapshots: List[PortfolioSnapshot],
        trade_history: List[Trade],
        bars: List[Bar],
        initial_capital: float,
        indicator_list: List[Indicator] = [],
    ):
        """
        Args:
            portfolio_snapshots (List[PortfolioSnapshot]): 回测引擎输出的投资组合快照列表。
            trade_history (List[Trade]): 回测引擎输出的交易历史记录列表。
            bars (List[Bar]): 回测引擎输出的所有K线数据列表 (可能包含多个合约的K线)。
            initial_capital (float): 初始资金。
        """
        self.portfolio_snapshots = portfolio_snapshots
        self.trade_history = trade_history
        self.initial_capital = initial_capital
        self.bars = bars  # 接收所有K线数据
        self._metrics_cache: Optional[Dict[str, Any]] = None
        self.indicator_list = indicator_list

        print("\n--- 结果分析器初始化完成 ---")

    def calculate_all_metrics(self) -> Dict[str, Any]:
        """
        计算所有关键绩效指标。
        如果已计算过则返回缓存结果，否则调用纯函数计算。
        """
        if self._metrics_cache is None:
            print("正在计算绩效指标...")
            self._metrics_cache = calculate_metrics(
                self.portfolio_snapshots, self.trade_history, self.initial_capital
            )
            print("绩效指标计算完成。")
        return self._metrics_cache

    def generate_report(self) -> None:
        """
        生成并打印详细的回测报告。
        """
        if self.trade_history:
            print("\n--- 交易明细 ---")
            for trade in self.trade_history:
                # 调整输出格式，显示实现盈亏
                pnl_display = (
                    f" | Indicators:{trade.indicator_dict} | PnL: {trade.realized_pnl:.2f}"
                    if trade.is_close_trade
                    else ""
                )
                print(
                    f"  {trade.fill_time} | {trade.direction:<10} | {trade.symbol} | Vol: {trade.volume} | Price: {trade.price:.2f} | Comm: {trade.commission:.2f}{pnl_display}"
                )
        else:
            print("\n没有交易记录。")

        metrics = self.calculate_all_metrics()

        print("\n--- 回测绩效报告 ---")
        print(f"{'初始资金':<15}: {metrics['初始资金']:.2f}")
        print(f"{'最终资金':<15}: {metrics['最终资金']:.2f}")
        print(f"{'总收益率':<15}: {metrics['总收益率']:.2%}")
        print(f"{'年化收益率':<15}: {metrics['年化收益率']:.2%}")
        print(f"{'最大回撤':<15}: {metrics['最大回撤']:.2%}")
        print(f"{'夏普比率':<15}: {metrics['夏普比率']:.2f}")
        print(f"{'卡玛比率':<15}: {metrics['卡玛比率']:.2f}")
        print(f"{'总交易次数':<15}: {metrics['总交易次数']}")
        print(f"{'总实现盈亏':<15}: {metrics['总实现盈亏']:.2f}")  # 新增
        print(f"{'交易成本':<15}: {metrics['交易成本']:.2f}")

        # 新增交易相关详细指标，以适应更全面的交易分析需求
        print("\n--- 交易详情 ---")
        print(f"{'盈利交易次数':<15}: {metrics['盈利交易次数']}")
        print(f"{'亏损交易次数':<15}: {metrics['亏损交易次数']}")
        print(f"{'胜率':<15}: {metrics['胜率']:.2%}")
        print(f"{'盈亏比':<15}: {metrics['盈亏比']:.2f}")
        print(f"{'平均每次盈利':<15}: {metrics['平均每次盈利']:.2f}")
        print(f"{'平均每次亏损':<15}: {metrics['平均每次亏损']:.2f}")

    def plot_performance(self) -> None:
        """
        绘制投资组合净值和回撤曲线，以及所有合约的收盘价曲线。
        """
        print("正在绘制绩效图表...")
        plot_equity_and_drawdown_chart(
            self.portfolio_snapshots,
            self.initial_capital,
            title="Portfolio Equity and Drawdown Curve (All Contracts)",  # 明确标题，表明是整体曲线
        )

        # 绘制所有处理过的K线收盘价曲线
        plot_close_price_chart(
            self.bars, title="Underlying Asset Close Price (Concatenated Bars)"
        )  # 明确标题
        print("图表绘制完成。")

    def analyze_indicators(self):
        """
        分析所有平仓交易的指标值与实现盈亏的关系，并绘制累积盈亏曲线图。
        图表将展示指标值区间与对应累积盈亏的关系，帮助找出具有概率优势的指标区间。
        同时会标记出最大和最小累积盈亏对应的指标值，并优化标注位置以避免重叠。
        """
        close_trades = [trade for trade in self.trade_history if trade.is_close_trade]

        if not close_trades:
            print(
                "没有平仓交易可供分析。请确保 trade_history 中有 is_close_trade 为 True 的交易。"
            )
            return

        for indicator in self.indicator_list:
            # 假设每个 indicator 对象都有一个 get_name() 方法
            indicator_name = indicator.get_name()

            # 收集该指标的所有值和对应的实现盈亏
            indi_values = []
            pnls = []
            for trade in close_trades:
                # 确保 trade.indicator_dict 中包含当前指标的值
                # 并且这个值是可用的（非None或NaN）
                if (
                    indicator_name in trade.indicator_dict
                    and trade.indicator_dict[indicator_name] is not None
                ):
                    # 检查是否为 NaN，如果使用 np.nan，则需要 isinstance(value, float) and np.isnan(value)
                    # 为了简化，这里假设非 None 即为有效数值
                    if not (
                        isinstance(trade.indicator_dict[indicator_name], float)
                        and np.isnan(trade.indicator_dict[indicator_name])
                    ):
                        indi_values.append(trade.indicator_dict[indicator_name])
                        pnls.append(trade.realized_pnl)

            if not indi_values:
                print(f"指标 '{indicator_name}' 没有对应的有效平仓交易数据。跳过绘图。")
                continue

            # 将收集到的数据转换为 Pandas DataFrame 进行更便捷的处理
            # DataFrame 的结构为：['indicator_value', 'realized_pnl']
            df = pd.DataFrame({"indicator_value": indi_values, "realized_pnl": pnls})

            # 确保数据框不为空
            if df.empty:
                print(f"指标 '{indicator_name}' 的数据框为空，跳过绘图。")
                continue

            # 按照指标值进行排序，这是计算累积和的关键步骤
            df = df.sort_values(by="indicator_value").reset_index(drop=True)

            # --- 绘制累积收益曲线 ---
            plt.figure(figsize=(12, 7))  # 创建一个新的图表

            # 获取指标值的范围，用于生成X轴的等距点
            min_val = df["indicator_value"].min()
            max_val = df["indicator_value"].max()

            # 特殊处理：如果所有指标值都相同
            if min_val == max_val:
                total_pnl = df["realized_pnl"].sum()
                print(
                    f"指标 '{indicator_name}' 的所有值都相同 ({min_val:.2f})，无法创建区间图，绘制一个点表示总收益。"
                )
                plt.plot(min_val, total_pnl, "ro", markersize=8)  # 绘制一个点
                plt.title(
                    f"{indicator_name} Value vs. Cumulative PnL (All values are {min_val:.2f})"
                )
                plt.xlabel(f"{indicator_name} Value")
                plt.ylabel("Cumulative Realized PnL")
                plt.grid(True)
                plt.text(
                    min_val,
                    total_pnl,
                    f" Total PnL: {total_pnl:.2f}",
                    ha="center",
                    va="bottom",
                )
                plt.show()
                continue

            # 生成X轴上的100个等距点，这些点代表了指标值的不同阈值
            x_points = np.linspace(min_val, max_val, 100)

            # 计算Y轴的值：对于每个X轴点 xp，Y轴值是所有 'indicator_value' <= xp 的 'realized_pnl' 之和
            y_cumulative_pnl = []
            for xp in x_points:
                # 筛选出指标值小于等于当前x_point的所有交易，并求和它们的 realized_pnl
                cumulative_pnl = df[df["indicator_value"] <= xp]["realized_pnl"].sum()
                y_cumulative_pnl.append(cumulative_pnl)

            # 绘制累积盈亏曲线
            plt.plot(
                x_points,
                y_cumulative_pnl,
                marker="o",
                linestyle="-",
                markersize=3,
                label=f"Cumulative PnL for {indicator_name}",
                alpha=0.8,
            )

            # 标记累积盈亏的最大值点
            optimal_index = np.argmax(y_cumulative_pnl)
            optimal_indi_value = x_points[optimal_index]
            max_cumulative_pnl = y_cumulative_pnl[optimal_index]

            # 标记累积盈亏的最小值点
            min_pnl_index = np.argmin(y_cumulative_pnl[:optimal_index]) if len(y_cumulative_pnl[:optimal_index]) > 0 else 0
            min_indi_value_at_pnl = x_points[min_pnl_index]
            min_cumulative_pnl = y_cumulative_pnl[min_pnl_index]

            # 动态调整标注位置以避免重叠
            offset_x = (max_val - min_val) * 0.05  # 水平偏移量

            # 默认标注为右侧对齐，文本在点的左侧
            max_ha = "right"
            max_xytext_x = optimal_indi_value - offset_x
            min_ha = "right"
            min_xytext_x = min_indi_value_at_pnl - offset_x

            # 如果最大值点在最小值点右侧，则最大值标注放左侧，最小值标注放右侧
            # 这样可以避免标注文本重叠
            if optimal_indi_value > min_indi_value_at_pnl:
                max_ha = "left"
                max_xytext_x = optimal_indi_value + offset_x
                min_ha = "right"
                min_xytext_x = min_indi_value_at_pnl - offset_x
            else:  # 如果最大值点在最小值点左侧或重合
                max_ha = "right"
                max_xytext_x = optimal_indi_value - offset_x
                min_ha = "left"
                min_xytext_x = min_indi_value_at_pnl + offset_x

            # 绘制最大值垂直线和标注
            plt.axvline(
                optimal_indi_value,
                color="red",
                linestyle="--",
                label=f"Max PnL Threshold: {optimal_indi_value:.2f}",
                alpha=0.7,
            )
            plt.annotate(
                f"Max Cum. PnL: {max_cumulative_pnl:.2f}",
                xy=(optimal_indi_value, max_cumulative_pnl),
                xytext=(max_xytext_x, max_cumulative_pnl),
                arrowprops=dict(facecolor="red", shrink=0.05),
                horizontalalignment=max_ha,
                verticalalignment="bottom",
                color="red",
            )

            # 绘制最小值垂直线和标注
            plt.axvline(
                min_indi_value_at_pnl,
                color="blue",
                linestyle=":",
                label=f"Min PnL Threshold: {min_indi_value_at_pnl:.2f}",
                alpha=0.7,
            )

            # 垂直偏移最小值标注，避免与曲线重叠
            min_text_y_offset = (
                -(max_cumulative_pnl - min_cumulative_pnl) * 0.1
                if max_cumulative_pnl != min_cumulative_pnl
                else -0.05
            )
            plt.annotate(
                f"Min Cum. PnL: {min_cumulative_pnl:.2f}",
                xy=(min_indi_value_at_pnl, min_cumulative_pnl),
                xytext=(min_xytext_x, min_cumulative_pnl + min_text_y_offset),
                arrowprops=dict(facecolor="blue", shrink=0.05),
                horizontalalignment=min_ha,
                verticalalignment="top",
                color="blue",
            )

            plt.title(f"{indicator_name} Value vs. Cumulative Realized PnL")
            plt.xlabel(f"{indicator_name} Value")
            plt.ylabel("Cumulative Realized PnL")
            plt.grid(True)
            plt.legend()
            plt.tight_layout()  # 自动调整图表参数，使之更紧凑
            plt.show()

        print("\n所有指标的分析图表已生成。")
