"""
风控熔断器

实时风控检查，在下单前和持仓期间执行:
1. 单日亏损 > 5% → 暂停交易
2. 总回撤 > 25% → 全部平仓
3. 保证金不足 → 拒绝开仓
4. 单币仓位超限 → 拒绝开仓
5. 总仓位超限 → 拒绝开仓

所有熔断事件都推送Telegram告警
"""

import logging
from typing import Optional
from datetime import datetime, timezone, date

from ..config import settings as S
from ..config.coins import get_coin
from ..core.position import calc_effective_equity
from ..data.database import Database
from ..notify.telegram_bot import TelegramNotifier

logger = logging.getLogger(__name__)


class RiskGuard:
    """
    风控熔断器

    用法:
        guard = RiskGuard(db, notifier)

        # 开仓前检查
        ok, reason = guard.pre_trade_check(
            balance=10000, margin_needed=2000,
            symbol='BTCUSDT', used_margin=3000
        )

        # 交易后更新
        guard.record_trade_result(pnl=-200, balance=9800)

        # 检查是否可交易
        if guard.is_halted:
            print("交易已暂停")
    """

    def __init__(self, db: Database, notifier: TelegramNotifier):
        self.db = db
        self.notifier = notifier

        # 状态
        self.daily_pnl = 0.0
        self.current_date: Optional[date] = None
        self.peak_balance = 0.0
        self.halted = False
        self.halt_reason = ''

        self._restore_state()

    def _restore_state(self):
        """从数据库恢复"""
        self.peak_balance = float(self.db.get_state('peak_balance', '0'))
        self.halted = self.db.get_state('risk_halted', '0') == '1'
        self.halt_reason = self.db.get_state('halt_reason', '')

        if self.halted:
            logger.warning(f"Risk guard: HALTED ({self.halt_reason})")

    def _save_state(self):
        """保存状态"""
        self.db.set_state('peak_balance', str(self.peak_balance))
        self.db.set_state('risk_halted', '1' if self.halted else '0')
        self.db.set_state('halt_reason', self.halt_reason)

    @property
    def is_halted(self) -> bool:
        """是否处于熔断状态"""
        # 新的一天自动解除日亏熔断
        today = datetime.now(timezone.utc).date()
        if self.current_date != today:
            self.current_date = today
            self.daily_pnl = 0.0
            if self.halted and self.halt_reason == 'daily_loss':
                self.halted = False
                self.halt_reason = ''
                self._save_state()
                logger.info("Daily loss halt released (new day)")

        return self.halted

    # ==================== 开仓前检查 ====================

    def pre_trade_check(
        self,
        balance: float,
        margin_needed: float,
        symbol: str,
        used_margin: float = 0,
        month_start_balance: float = 0,
    ) -> tuple:
        """
        开仓前风控检查

        Returns:
            (passed: bool, reason: str)
        """
        # 0. 熔断状态检查
        if self.is_halted:
            return False, f"熔断中: {self.halt_reason}"

        # 1. 余额检查
        if balance <= 0:
            return False, "余额<=0"

        # 2. 保证金检查
        if margin_needed > balance:
            return False, f"保证金${margin_needed:,.0f} > 余额${balance:,.0f}"

        # 3. 有效权益检查
        if month_start_balance > 0:
            effective = calc_effective_equity(balance, month_start_balance)
            if margin_needed > effective * S.SINGLE_COIN_LIMIT:
                return False, f"超过有效权益单币上限"

        # 4. 单币仓位上限
        if margin_needed > balance * S.SINGLE_COIN_LIMIT:
            return False, f"单币仓位超过{S.SINGLE_COIN_LIMIT:.0%}"

        # 5. 总仓位上限
        total_margin = used_margin + margin_needed
        if total_margin > balance * S.TOTAL_POSITION_LIMIT:
            return False, f"总仓位超过{S.TOTAL_POSITION_LIMIT:.0%}"

        # 6. 回撤检查
        if self.peak_balance > 0:
            dd = (self.peak_balance - balance) / self.peak_balance
            if dd > S.TOTAL_DD_HALT * 0.8:  # 接近熔断线时警告
                logger.warning(f"Approaching drawdown limit: {dd:.1%}")

        return True, "通过"

    # ==================== 交易结果更新 ====================

    def record_trade_result(self, pnl: float, balance: float):
        """
        记录交易结果，检查是否触发熔断

        Args:
            pnl: 本笔盈亏
            balance: 当前余额
        """
        # 更新峰值
        self.peak_balance = max(self.peak_balance, balance)

        # 日内盈亏
        today = datetime.now(timezone.utc).date()
        if self.current_date != today:
            self.current_date = today
            self.daily_pnl = 0.0

        self.daily_pnl += pnl

        # 检查日亏熔断
        if balance > 0 and self.daily_pnl / balance < -S.DAILY_LOSS_HALT:
            self._trigger_halt('daily_loss',
                               f"单日亏损{self.daily_pnl/balance:.1%} > {S.DAILY_LOSS_HALT:.0%}")

        # 检查总回撤熔断
        if self.peak_balance > 0:
            dd = (self.peak_balance - balance) / self.peak_balance
            if dd > S.TOTAL_DD_HALT:
                self._trigger_halt('total_drawdown',
                                   f"总回撤{dd:.1%} > {S.TOTAL_DD_HALT:.0%}")

        self._save_state()

    def _trigger_halt(self, reason: str, message: str):
        """触发熔断"""
        self.halted = True
        self.halt_reason = reason
        self._save_state()

        logger.critical(f"RISK HALT: {message}")
        self.notifier.send_sync(
            f"🚨 风控熔断!\n"
            f"原因: {message}\n"
            f"所有新开仓已暂停\n"
            f"{'日亏熔断次日自动解除' if reason=='daily_loss' else '需手动解除'}"
        )

    # ==================== 手动操作 ====================

    def manual_resume(self):
        """手动解除熔断"""
        self.halted = False
        self.halt_reason = ''
        self._save_state()
        logger.info("Risk halt manually released")
        self.notifier.send_sync("✅ 风控熔断已手动解除")

    def reset_peak(self, new_peak: float):
        """重置峰值余额（慎用）"""
        self.peak_balance = new_peak
        self._save_state()
        logger.info(f"Peak balance reset to ${new_peak:,.0f}")
