"""
订单管理器 — 交易生命周期管理

职责:
1. 接收信号 → 计算下单参数 → 开仓
2. 开仓后立即挂止损单（不依赖程序运行）
3. 每4H检查trailing → 调整止损位
4. 记录所有操作到数据库

关键安全原则:
- 止损单在开仓后立即挂出
- 断网/异常时不开新仓，只管理已有持仓
- 每笔操作都推送Telegram通知
"""

import logging
import time
from typing import Optional, Dict, List
from datetime import datetime, timezone
from dataclasses import dataclass, field

from .binance_client import BinanceClient, BinanceAPIError
from ..config.coins import get_coin, COINS
from ..config import settings as S
from ..core.position import calc_position, calc_effective_equity
from ..data.database import Database
from ..notify.telegram_bot import TelegramNotifier

logger = logging.getLogger(__name__)


@dataclass
class ActiveTrade:
    """活跃持仓记录"""
    trade_id: str              # 唯一标识
    symbol: str
    direction: str             # 'long' or 'short'
    signal_type: str
    entry_price: float
    entry_time: str
    quantity: float            # 币数量
    notional: float            # 名义持仓
    margin: float              # 保证金
    leverage: int
    stop_loss: float           # 当前止损价
    stop_loss_order_id: Optional[int] = None
    trailing_active: bool = False
    peak_price: float = 0      # 做多最高价
    trough_price: float = 999999  # 做空最低价
    entry_method: str = ''
    bars_held: int = 0


class OrderManager:
    """
    订单管理器

    用法:
        om = OrderManager(client, db, notifier)

        # 开仓
        trade = om.open_position(signal_dict)

        # 定期检查trailing
        om.check_all_trailing()

        # 手动平仓
        om.close_position(trade_id)

        # 紧急全部平仓
        om.emergency_close_all()
    """

    def __init__(
        self,
        client: BinanceClient,
        db: Database,
        notifier: TelegramNotifier,
        balance: float = 0,
        month_start_balance: float = 0,
    ):
        self.client = client
        self.db = db
        self.notifier = notifier
        self.balance = balance or self._fetch_balance()
        self.month_start_balance = month_start_balance or self.balance

        # 活跃持仓
        self.active_trades: Dict[str, ActiveTrade] = {}

        # 从数据库恢复
        self._restore_trades()

    def _fetch_balance(self) -> float:
        """从Binance获取余额"""
        try:
            return self.client.get_balance()
        except Exception as e:
            logger.error(f"Failed to fetch balance: {e}")
            return float(self.db.get_state('balance', '1000'))

    def _restore_trades(self):
        """从数据库恢复活跃持仓"""
        import json
        trades_json = self.db.get_state('active_trades', '{}')
        try:
            trades_data = json.loads(trades_json)
            for tid, td in trades_data.items():
                self.active_trades[tid] = ActiveTrade(**td)
            if self.active_trades:
                logger.info(f"Restored {len(self.active_trades)} active trades")
        except Exception as e:
            logger.warning(f"Failed to restore trades: {e}")

    def _save_trades(self):
        """保存活跃持仓到数据库"""
        import json
        data = {}
        for tid, trade in self.active_trades.items():
            data[tid] = {
                'trade_id': trade.trade_id,
                'symbol': trade.symbol,
                'direction': trade.direction,
                'signal_type': trade.signal_type,
                'entry_price': trade.entry_price,
                'entry_time': trade.entry_time,
                'quantity': trade.quantity,
                'notional': trade.notional,
                'margin': trade.margin,
                'leverage': trade.leverage,
                'stop_loss': trade.stop_loss,
                'stop_loss_order_id': trade.stop_loss_order_id,
                'trailing_active': trade.trailing_active,
                'peak_price': trade.peak_price,
                'trough_price': trade.trough_price,
                'entry_method': trade.entry_method,
                'bars_held': trade.bars_held,
            }
        self.db.set_state('active_trades', json.dumps(data))

    # ==================== 开仓 ====================

    def open_position(self, signal: dict) -> Optional[ActiveTrade]:
        """
        根据信号开仓

        流程:
        1. 设置杠杆和保证金模式
        2. 计算下单数量
        3. 市价/限价下单
        4. 立即挂止损单
        5. 推送通知
        6. 记录数据库

        Args:
            signal: 信号字典（来自scanner）
        Returns:
            ActiveTrade or None if failed
        """
        symbol = signal['coin']
        direction = signal['direction']
        cfg = get_coin(symbol)

        try:
            # 1. 设置杠杆
            self.client.set_leverage(symbol, cfg.leverage)
            self.client.set_margin_type(symbol, 'ISOLATED')

            # 2. 检查是否已有持仓
            existing = self.client.get_position(symbol)
            if existing:
                logger.warning(f"{symbol} already has position, skip")
                return None

            # 3. 计算数量
            notional = signal.get('notional', 0)
            if notional <= 0:
                logger.warning(f"Invalid notional: {notional}")
                return None

            quantity = self.client.calc_quantity(symbol, notional)
            if quantity <= 0:
                return None

            # 4. 下单
            side = 'BUY' if direction == 'long' else 'SELL'
            entry_method = signal.get('entry_method', '4H直接')

            if entry_method == '1H回踩':
                # 限价单
                order = self.client.limit_open(
                    symbol, side, quantity, signal['entry_price']
                )
            else:
                # 市价单
                order = self.client.market_open(symbol, side, quantity)

            if not order or 'orderId' not in order:
                logger.error(f"Open order failed: {order}")
                return None

            # 等待成交
            entry_price = float(order.get('avgPrice', signal['entry_price']))
            if entry_price == 0:
                entry_price = signal['entry_price']

            # 5. 立即挂止损单
            sl_side = 'SELL' if direction == 'long' else 'BUY'
            stop_loss = signal['stop_loss']

            try:
                sl_order = self.client.stop_loss_order(
                    symbol, sl_side, stop_loss, quantity
                )
                sl_order_id = sl_order.get('orderId')
            except Exception as e:
                logger.error(f"Stop loss order failed: {e}")
                sl_order_id = None
                # 止损挂单失败是严重问题，通知用户
                self.notifier.send_sync(
                    f"⚠️ 止损挂单失败!\n{symbol} {direction}\n"
                    f"请手动设置止损 @ ${stop_loss:,.2f}\n"
                    f"错误: {e}"
                )

            # 6. 创建交易记录
            trade_id = f"{symbol}_{int(time.time())}"
            trade = ActiveTrade(
                trade_id=trade_id,
                symbol=symbol,
                direction=direction,
                signal_type=signal['signal_type'],
                entry_price=entry_price,
                entry_time=datetime.now(timezone.utc).isoformat(),
                quantity=quantity,
                notional=notional,
                margin=signal.get('margin', notional / cfg.leverage),
                leverage=cfg.leverage,
                stop_loss=stop_loss,
                stop_loss_order_id=sl_order_id,
                peak_price=entry_price,
                trough_price=entry_price,
                entry_method=entry_method,
            )

            self.active_trades[trade_id] = trade
            self._save_trades()

            # 7. 通知
            self.notifier.send_sync(
                f"✅ 已开仓\n"
                f"{'🟢 做多' if direction=='long' else '🔴 做空'} {symbol}\n"
                f"入场: ${entry_price:,.2f}\n"
                f"止损: ${stop_loss:,.2f} ({'已挂单' if sl_order_id else '⚠️ 挂单失败'})\n"
                f"数量: {quantity}\n"
                f"名义: ${notional:,.0f} ({cfg.leverage}x)"
            )

            logger.info(f"Opened {direction} {symbol}: "
                        f"qty={quantity} @ {entry_price}, SL={stop_loss}")
            return trade

        except Exception as e:
            logger.error(f"Open position failed: {e}", exc_info=True)
            self.notifier.send_sync(f"⚠️ 开仓失败: {symbol}\n{e}")
            return None

    # ==================== Trailing 止损管理 ====================

    def check_all_trailing(self):
        """
        检查所有活跃持仓的trailing止损

        应每4H调用一次
        """
        if not self.active_trades:
            return

        logger.info(f"Checking trailing for {len(self.active_trades)} positions")

        for trade_id, trade in list(self.active_trades.items()):
            try:
                self._check_trailing_single(trade)
            except Exception as e:
                logger.error(f"Trailing check error {trade.symbol}: {e}")

        self._save_trades()

    def _check_trailing_single(self, trade: ActiveTrade):
        """检查单个持仓的trailing"""
        cfg = get_coin(trade.symbol)
        current_price = self.client.get_price(trade.symbol)

        trade.bars_held += 1

        if trade.direction == 'long':
            # 更新最高价
            trade.peak_price = max(trade.peak_price, current_price)

            # 检查浮盈是否达到激活条件
            unrealized = (trade.peak_price - trade.entry_price) / trade.entry_price
            if unrealized >= S.TRAILING_ACTIVATE_PCT:
                trade.trailing_active = True

            if trade.trailing_active:
                # 计算新止损位
                trailing_pct = cfg.trailing_pct
                new_sl = trade.peak_price * (1 - trailing_pct)

                if new_sl > trade.stop_loss:
                    self._update_stop_loss(trade, new_sl)

            # 最长持仓检查
            if trade.bars_held >= S.MAX_HOLD_LONG_4H_BARS:
                self._close_trade(trade, 'max_hold')

        else:  # short
            trade.trough_price = min(trade.trough_price, current_price)

            unrealized = (trade.entry_price - trade.trough_price) / trade.entry_price
            if unrealized >= S.TRAILING_ACTIVATE_PCT:
                trade.trailing_active = True

            if trade.trailing_active:
                new_sl = trade.trough_price * (1 + 0.12)  # 做空trailing 12%

                if new_sl < trade.stop_loss:
                    self._update_stop_loss(trade, new_sl)

            if trade.bars_held >= S.MAX_HOLD_SHORT_4H_BARS:
                self._close_trade(trade, 'max_hold')

    def _update_stop_loss(self, trade: ActiveTrade, new_sl: float):
        """更新止损位"""
        try:
            # 撤销旧止损单
            if trade.stop_loss_order_id:
                try:
                    self.client.cancel_order(trade.symbol, trade.stop_loss_order_id)
                except BinanceAPIError:
                    pass  # 可能已触发

            # 挂新止损单
            sl_side = 'SELL' if trade.direction == 'long' else 'BUY'
            sl_order = self.client.stop_loss_order(
                trade.symbol, sl_side, new_sl, trade.quantity
            )

            old_sl = trade.stop_loss
            trade.stop_loss = new_sl
            trade.stop_loss_order_id = sl_order.get('orderId')

            logger.info(f"Updated SL {trade.symbol}: "
                        f"${old_sl:,.2f} → ${new_sl:,.2f}")

            self.notifier.send_sync(
                f"📊 止损上移\n"
                f"{trade.symbol} {'做多' if trade.direction=='long' else '做空'}\n"
                f"旧止损: ${old_sl:,.2f}\n"
                f"新止损: ${new_sl:,.2f}\n"
                f"当前浮盈: {self._calc_unrealized(trade):.1%}"
            )

        except Exception as e:
            logger.error(f"Update SL failed {trade.symbol}: {e}")

    def _calc_unrealized(self, trade: ActiveTrade) -> float:
        """计算未实现盈亏百分比"""
        try:
            price = self.client.get_price(trade.symbol)
            if trade.direction == 'long':
                return (price - trade.entry_price) / trade.entry_price
            else:
                return (trade.entry_price - price) / trade.entry_price
        except:
            return 0.0

    # ==================== 平仓 ====================

    def _close_trade(self, trade: ActiveTrade, reason: str):
        """平仓"""
        try:
            # 撤销所有挂单
            self.client.cancel_all_orders(trade.symbol)

            # 市价平仓
            close_side = 'SELL' if trade.direction == 'long' else 'BUY'
            order = self.client.market_close(
                trade.symbol, close_side, trade.quantity
            )

            exit_price = float(order.get('avgPrice', 0))
            if exit_price == 0:
                exit_price = self.client.get_price(trade.symbol)

            # 计算盈亏
            if trade.direction == 'long':
                pnl_pct = (exit_price - trade.entry_price) / trade.entry_price
            else:
                pnl_pct = (trade.entry_price - exit_price) / trade.entry_price
            pnl_dollar = trade.notional * pnl_pct

            # 通知
            emoji = '💰' if pnl_dollar > 0 else '❌'
            self.notifier.send_sync(
                f"{emoji} 已平仓 ({reason})\n"
                f"{trade.symbol} {'做多' if trade.direction=='long' else '做空'}\n"
                f"入场: ${trade.entry_price:,.2f}\n"
                f"出场: ${exit_price:,.2f}\n"
                f"盈亏: ${pnl_dollar:+,.2f} ({pnl_pct:+.1%})\n"
                f"持仓: {trade.bars_held * 4}小时"
            )

            # 更新余额
            self.balance = self._fetch_balance()
            self.db.set_state('balance', str(self.balance))

            # 移除活跃持仓
            if trade.trade_id in self.active_trades:
                del self.active_trades[trade.trade_id]
            self._save_trades()

            logger.info(f"Closed {trade.symbol}: {reason}, PnL=${pnl_dollar:+,.2f}")

        except Exception as e:
            logger.error(f"Close trade failed {trade.symbol}: {e}")
            self.notifier.send_sync(f"⚠️ 平仓失败: {trade.symbol}\n{e}")

    def close_position(self, trade_id: str, reason: str = 'manual'):
        """手动平仓"""
        trade = self.active_trades.get(trade_id)
        if trade:
            self._close_trade(trade, reason)
        else:
            logger.warning(f"Trade {trade_id} not found")

    def emergency_close_all(self):
        """紧急全部平仓"""
        logger.warning("EMERGENCY: Closing all positions!")
        self.notifier.send_sync("🚨 紧急全部平仓!")

        for trade_id, trade in list(self.active_trades.items()):
            try:
                self._close_trade(trade, 'emergency')
            except Exception as e:
                logger.error(f"Emergency close {trade.symbol} failed: {e}")

    # ==================== 对账 ====================

    def reconcile(self):
        """
        对账：API持仓 vs 本地记录

        每日执行，确保本地记录与交易所一致
        """
        api_positions = self.client.get_positions()
        api_symbols = {p['symbol'] for p in api_positions}
        local_symbols = {t.symbol for t in self.active_trades.values()}

        # 本地有但API没有 → 可能已被止损平仓
        for tid, trade in list(self.active_trades.items()):
            if trade.symbol not in api_symbols:
                logger.warning(f"Reconcile: {trade.symbol} not in API, "
                               "likely stop loss triggered")
                self.notifier.send_sync(
                    f"📋 对账: {trade.symbol} 已被止损平仓\n"
                    f"（API无此持仓）"
                )
                del self.active_trades[tid]

        # API有但本地没有 → 异常，通知用户
        for p in api_positions:
            if p['symbol'] not in local_symbols:
                logger.warning(f"Reconcile: {p['symbol']} in API but not local!")
                self.notifier.send_sync(
                    f"⚠️ 对账异常: {p['symbol']} 在交易所有持仓但本地无记录\n"
                    f"方向: {p['side']}, 数量: {p['quantity']}\n"
                    f"请检查！"
                )

        self._save_trades()
        self.balance = self._fetch_balance()
        self.db.set_state('balance', str(self.balance))

        logger.info(f"Reconcile done: {len(self.active_trades)} local, "
                    f"{len(api_positions)} API")

    # ==================== 状态查询 ====================

    def get_status(self) -> dict:
        """获取当前状态"""
        return {
            'balance': self.balance,
            'active_trades': len(self.active_trades),
            'total_margin': sum(t.margin for t in self.active_trades.values()),
            'trades': [
                {
                    'symbol': t.symbol,
                    'direction': t.direction,
                    'entry_price': t.entry_price,
                    'stop_loss': t.stop_loss,
                    'trailing_active': t.trailing_active,
                    'bars_held': t.bars_held,
                    'unrealized': self._calc_unrealized(t),
                }
                for t in self.active_trades.values()
            ],
        }
