"""
策略引擎核心模块 — 策略基类、配置与信号定义

提供策略开发的抽象基类和核心数据类型，所有策略必须继承 BaseStrategy。
"""

from abc import ABC, abstractmethod
from typing import Optional

from pydantic import BaseModel

from .models import Kline, OrderBook, Ticker, Trade


# ============================================================
# 配置模型
# ============================================================


class StrategyConfig(BaseModel):
    """策略配置基类

    由具体策略子类化扩展，添加策略专属参数。
    子类示例：
        class MACrossConfig(StrategyConfig):
            fast_period: int = 7
            slow_period: int = 25
    """
    name: str = ""
    symbol: str = ""
    exchange: str = "binance"
    type: str = "spot"                      # 交易对类型：spot / um / cm
    enabled: bool = True
    max_position_pct: float = 0.1          # 最大仓位占总资金比例
    stop_loss_pct: Optional[float] = None  # 止损百分比
    take_profit_pct: Optional[float] = None  # 止盈百分比
    cooldown_seconds: float = 1.0          # 下单冷却时间（秒）
    min_confidence: float = 0.3            # 最低信号置信度


# ============================================================
# 信号模型
# ============================================================


class Signal(BaseModel):
    """交易信号

    策略通过返回 Signal 来表达交易意图，由信号分发器路由到交易执行模块。
    """
    symbol: str
    side: str                                      # "BUY" / "SELL"
    signal_type: str = "MARKET"                     # "MARKET" / "LIMIT" / "CANCEL"
    price: Optional[float] = None                   # 限价单价格（signal_type=LIMIT 时必填）
    quantity: Optional[float] = None                # 下单数量（None 表示按仓位比例计算）
    confidence: float = 1.0                         # 信号置信度 [0, 1]
    reason: str = ""                                # 信号生成原因（便于审计和调试）
    timestamp: float = 0.0                          # 信号时间戳（Unix 毫秒）


# ============================================================
# 策略基类
# ============================================================


class BaseStrategy(ABC):
    """所有策略的抽象基类

    子类必须：
    1. 覆盖 strategy_type 类属性（唯一标识）
    2. 实现 on_kline() 方法
    3. 如有需要，覆盖其他 on_* 方法

    生命周期：
        register → create → on_start() → [事件循环] → on_stop() → unload

    用法示例：
        class MyStrategy(BaseStrategy):
            strategy_type = "my_strategy"

            async def on_kline(self, kline):
                if kline.close > self._ma:
                    return Signal(symbol=self.config.symbol, side="BUY",
                                  reason="Price above MA", timestamp=kline.open_time)
                return None
    """

    # 策略类型标识，子类必须覆盖为唯一字符串
    strategy_type: str = "base"

    def __init__(self, config: StrategyConfig):
        self.config = config
        self.is_running: bool = False
        self.pnl: float = 0.0
        self.trade_count: int = 0

    # ── 属性 ──

    @property
    def name(self) -> str:
        """策略名称（来自 config.name）"""
        return self.config.name

    # ── 生命周期钩子 ──

    async def on_start(self) -> None:
        """策略启动时调用。

        用于加载初始状态、从数据库恢复持仓信息、预热指标等。
        调用时机：策略实例创建后首次启动，或从暂停状态恢复时。
        """
        self.is_running = True

    async def on_stop(self) -> None:
        """策略停止时调用。

        用于清理资源、平仓（可选）、保存状态等。
        调用时机：手动停止、系统关闭、策略热更新前。
        """
        self.is_running = False

    async def on_pause(self) -> None:
        """策略暂停时调用。

        暂停期间策略不再接收行情数据，但保留内部状态。
        调用时机：手动暂停、风控触发暂停。
        """
        pass

    async def on_resume(self) -> None:
        """策略恢复时调用。

        从暂停状态恢复，重新开始接收行情数据。
        调用时机：手动恢复、风控解除。
        """
        pass

    # ── 行情事件处理器 ──

    @abstractmethod
    async def on_kline(self, kline: Kline) -> Optional[Signal]:
        """处理 K 线数据。

        K 线是最主要的行情输入，子类必须实现此方法。
        返回 Signal 表示产生交易意图，返回 None 表示不交易。

        Args:
            kline: engine.common.models.Kline 实例

        Returns:
            Signal | None
        """
        ...

    async def on_ticker(self, ticker: Ticker) -> Optional[Signal]:
        """处理 Ticker 数据（可选实现）。

        对于高频或需要实时价格的策略，可覆盖此方法。
        """
        return None

    async def on_trade(self, trade: Trade) -> Optional[Signal]:
        """处理逐笔成交数据（可选实现）。

        用于需要分析成交量分布、大单检测等场景。
        """
        return None

    async def on_orderbook(self, orderbook: OrderBook) -> Optional[Signal]:
        """处理订单簿深度数据（可选实现）。

        用于需要分析盘口深度、流动性等的策略。
        """
        return None

    # ── 交易反馈钩子 ──

    async def on_order_filled(self, order_result) -> None:
        """订单成交回调。

        当策略发出的订单被成交后，交易执行模块通过此钩子通知策略。
        策略可据此更新内部状态（持仓、盈亏等）。

        Args:
            order_result: 订单成交结果（字段取决于 executor 模块定义）
        """
        pass

    async def on_order_cancelled(self, order_id: str) -> None:
        """订单撤销回调。

        Args:
            order_id: 被撤销的订单 ID
        """
        pass

    async def on_order_rejected(self, order_id: str, reason: str) -> None:
        """订单被拒回调。

        当风控检查不通过或交易所拒绝订单时触发。

        Args:
            order_id: 订单 ID
            reason: 拒绝原因
        """
        pass

    # ── 辅助方法 ──

    def log(self, level: str, message: str, **kwargs) -> None:
        """结构化日志输出。

        策略内应使用此方法而非直接 print 或使用 logging，
        以确保日志格式统一、可被监控系统采集。

        Args:
            level: debug / info / warning / error
            message: 日志消息
            **kwargs: 附加的结构化字段（如 price=50000, signal="BUY"）
        """
        from .logger import logger
        log_method = getattr(logger, level, logger.info)
        extras = " | ".join(f"{k}={v}" for k, v in kwargs.items()) if kwargs else ""
        full_msg = f"[{self.name}] {message}"
        if extras:
            full_msg += f" | {extras}"
        log_method(full_msg)
