import os
import random
import time
from datetime import datetime
from config import Config
from core.base_trader import BaseTrader
from xtquant.xttrader import XtQuantTrader
from xtquant.xttype import StockAccount
from xtquant import xtconstant
from xtquant.xtdata import get_instrument_detail
from logger_config import get_logger
from utils.mail_util import MailUtil
from core.trade_constants import (
    ORDER_STATUS_COMPLETED,
    ORDER_STATUS_CANCELLED,
    ORDER_STATUS_PENDING,
    ORDER_STATUS_PARTIAL,
    ORDER_TYPE_LIMIT,
    ORDER_TYPE_MARKET,
    ORDER_DIRECTION_BUY,
    ORDER_DIRECTION_SELL,
    ORDER_STATUS_FAILED
)
from core.local_order import LocalOrder

# 获取日志记录器
logger = get_logger('real_trader')

class MyXtQuantTraderCallback:
    def __init__(self, trader_instance):
        self.trader_instance: XtTrader = trader_instance
    def on_connected(self):
        logger.info("连接成功")
    def on_disconnected(self):
        """连接断开回调
        
        当交易连接断开时调用，会自动尝试重连
        如果重连失败，将设置连接状态为失败，并通过邮件通知
        """
        logger.warning("连接断开")
        if self.trader_instance:
            # 设置连接状态
            self.trader_instance.connected = False
            self.trader_instance.subscribed = False
            
            # 尝试重连
            if not self.trader_instance.reconnect():
                logger.error("重连失败")
                # 通知重连失败
                self.trader_instance.connection_failed = True
                self.trader_instance.last_reconnect_time = time.time()
                self.trader_instance.notify_connection_failure()
        else:
            logger.error("重连失败: trader_instance为空")
    def on_account_status(self, status):
        pass
    def on_stock_asset(self, asset):
        logger.info(f"资金变动: {asset.account_id} {asset.cash} {asset.total_asset}")
    def on_stock_order(self, order):
        logger.warning(f"委托回报变化: ID={order.order_id} 状态={order.order_status}")
    def on_stock_trade(self, trade):
        logger.warning(f"成交变动: 成交时间={trade.traded_time} 订单编号={trade.order_id} 委托类型={trade.order_type} 股票代码={trade.stock_code} 成交数量={trade.traded_volume} 成交价格={trade.traded_price} 成交金额={trade.traded_amount} 手续费={trade.commission}")
    def on_stock_position(self, position):
        logger.info(f"持仓变动: 股票代码={position.stock_code} 持仓数量={position.volume} 持仓成本={position.avg_price}")
    def on_order_error(self, order_error):
        logger.error(f"委托失败: {order_error.order_id} {order_error.error_id} {order_error.error_msg}")
    def on_cancel_error(self, cancel_error):
        logger.error(f"撤单失败: {cancel_error.order_id} {cancel_error.error_id} {cancel_error.error_msg}")
    def on_order_stock_async_response(self, response):
        logger.info(f"异步下单反馈: {response.order_id}")
    def on_cancel_order_stock_async_response(self, response):
        logger.info(f"异步撤单反馈: {response.order_id}")
    def on_smt_appointment_async_response(self, response):
        logger.info(f"约券异步反馈: {response.seq}")

class XtTrader(BaseTrader):
    def __init__(self, connect_failed_callback=None):
        super().__init__(logger)
        self.started = False
        self.connected = False
        self.subscribed = False
        self._ACCOUNT = Config.XT_ACCOUNT
        self._PATH = Config.XT_PATH
        self._SESSION_ID = random.randint(Config.XT_MIN_SESSION_ID, Config.XT_MAX_SESSION_ID)
        self._account_type = os.environ.get("XT_ACCOUNT_TYPE", "STOCK")
        self._strategy_name = os.environ.get("XT_STRATEGY_NAME", "xt_strategy")
        self._remark = os.environ.get("XT_REMARK", "remark")
        
        # 重连相关
        self.connection_failed = False
        self.last_reconnect_time = None
        self.reconnect_interval = Config.XT_RECONNECT_INTERVAL
        self.connect_failed_callback = connect_failed_callback
        self.connection_error_message = None
        
        # 初始化trader
        self._callback = MyXtQuantTraderCallback(self)
        self.xt_trader = XtQuantTrader(self._PATH, self._SESSION_ID)
        self.account = StockAccount(self._ACCOUNT, self._account_type)
        self.xt_trader.register_callback(self._callback)

    def is_logged_in(self):
        """检查交易系统是否已经登录
        
        Returns:
            bool: True表示已登录，False表示未登录
        """
        return self.started and self.connected and self.subscribed
    
    def is_available(self):
        """检查交易接口是否可用
        
        Returns:
            bool: True表示可用，False表示不可用
        """
        return self.is_logged_in() and not self.connection_failed

    def login(self):
        """尝试登录交易系统
        
        Returns:
            bool: 登录是否成功
        """
        success = True
        
        try:
            if not self.started:
                self.xt_trader.start()
                self.started = True
                
            if not self.connected:
                result = self.xt_trader.connect()
                self.connected = (result == 0)
                if not self.connected:
                    success = False
                    
            if not self.subscribed and self.connected:
                result = self.xt_trader.subscribe(self.account)
                self.subscribed = (result == 0)
                if not self.subscribed:
                    success = False
                    
            # 登录失败，设置失败状态
            if not success:
                self.connection_failed = True
                self.last_reconnect_time = time.time()
                self.notify_connection_failure("登录失败")
                
            return success
        except Exception as e:
            logger.error(f"登录异常: {str(e)}")
            # 设置失败状态
            self.connection_failed = True
            self.last_reconnect_time = time.time()
            self.notify_connection_failure(f"登录异常: {str(e)}")
            return False

    def logout(self):
        if self.started:
            self.xt_trader.stop()
            self.started = False
            self.connected = False
            self.subscribed = False

    def get_balance(self):
        if not self.is_available():
            return None
            
        asset = self.xt_trader.query_stock_asset(self.account)
        if asset:
            return {
                "account_id": asset.account_id,
                "cash": asset.cash,
                "frozen_cash": asset.frozen_cash,
                "market_value": asset.market_value,
                "total_asset": asset.total_asset
            }
        return None

    def get_positions(self):
        if not self.is_available():
            return []
            
        positions = self.xt_trader.query_stock_positions(self.account)
        if positions:
            return [
                {
                    "account_id": p.account_id,
                    "stock_code": p.stock_code,
                    "volume": p.volume,
                    "can_use_volume": p.can_use_volume,
                    "open_price": p.open_price,
                    "market_value": p.market_value,
                    "frozen_volume": p.frozen_volume,
                    "on_road_volume": p.on_road_volume,
                    "yesterday_volume": p.yesterday_volume,
                    "avg_price": p.avg_price
                } for p in positions
            ]
        return []

    def get_position(self, stock_code):
        if not self.is_available():
            return None
            
        position = self.xt_trader.query_stock_position(self.account, stock_code)
        if position:
            return {
                "account_id": position.account_id,
                "stock_code": position.stock_code,
                "volume": position.volume,
                "can_use_volume": position.can_use_volume,
                "open_price": position.open_price, 
                "market_value": position.market_value,
                "frozen_volume": position.frozen_volume,
                "on_road_volume": position.on_road_volume,
                "yesterday_volume": position.yesterday_volume,
                "avg_price": position.avg_price
            }
        return None

    def get_today_trades(self):
        if not self.is_available():
            return []
            
        trades = self.xt_trader.query_stock_trades(self.account)
        if trades:
            return [
                {
                    "account_id": t.account_id,
                    "stock_code": t.stock_code,
                    "stock_name": self.get_stock_name(t.stock_code),
                    "order_id": t.order_id,
                    "traded_id": t.traded_id,
                    "order_sysid": t.order_sysid,
                    "traded_time": t.traded_time,
                    "traded_price": t.traded_price,
                    "traded_volume": t.traded_volume,
                    "traded_amount": t.traded_amount,
                    "trade_type": "buy" if t.order_type == xtconstant.STOCK_BUY else "sell"
                } for t in trades
            ]
        return []
    

    def get_trade(self, order_sysid):
        trades = self.get_today_trades()
        for t in trades:
            if t['order_sysid'] == order_sysid:
                return t
        return None


    def get_today_orders(self):
        if not self.is_available():
            return []
            
        orders = self.xt_trader.query_stock_orders(self.account)
        if orders:
            return [
                {
                    "account_id": o.account_id,
                    "stock_code": o.stock_code,
                    "order_id": o.order_id,
                    "order_sysid": o.order_sysid,
                    "order_time": o.order_time,
                    "order_type": "buy" if o.order_type == xtconstant.STOCK_BUY else "sell",
                    "order_volume": o.order_volume,
                    "price_type": self._convert_price_type(o.price_type),
                    "price": o.price,
                    "traded_volume": o.traded_volume,
                    "traded_price": o.traded_price,
                    "order_status": o.order_status,
                    "status_msg": o.status_msg
                } for o in orders
            ]
        return []

    def get_order(self, order_id):
        if not self.is_available():
            return None
            
        order = self.xt_trader.query_stock_order(self.account, int(order_id))
        if order:
            return {
                "account_id": order.account_id,
                "stock_code": order.stock_code,
                "order_id": order.order_id,
                "order_sysid": order.order_sysid,
                "order_time": order.order_time,
                "order_type": "buy" if order.order_type == xtconstant.STOCK_BUY else "sell",
                "order_volume": order.order_volume,
                "price_type": self._convert_price_type(order.price_type),
                "price": order.price,
                "traded_volume": order.traded_volume,
                "traded_price": order.traded_price,
                "order_status": order.order_status,
                "status_msg": order.status_msg
            }
        else:
            return None

    def _convert_price_type(self, price_type):
        """Convert numeric price type to readable string"""
        price_type_map = {
            xtconstant.LATEST_PRICE: "latest_price",           # 最新价
            xtconstant.FIX_PRICE: "limit_price",              # 指定价/限价
            xtconstant.MARKET_BEST: "market_best",            # 市价最优价
            xtconstant.MARKET_CANCEL: "market_cancel",        # 市价即成剩撤
            xtconstant.MARKET_CANCEL_ALL: "market_cancel_all",  # 市价全额成交或撤销
            xtconstant.MARKET_PEER_PRICE_FIRST: "market_peer_best",  # 对手方最优价格
            xtconstant.MARKET_MINE_PRICE_FIRST: "market_mine_best",  # 本方最优价格
        }
        return price_type_map.get(price_type, f"unknown_{price_type}")

    def get_stock_name(self, stock_code):
        """获取股票名称
        
        Args:
            stock_code: 股票代码，例如 "600000.SH"
            
        Returns:
            str: 股票名称，如果获取失败则返回空字符串
        """
        try:
            instrument_info = get_instrument_detail(stock_code)
            if instrument_info and "InstrumentName" in instrument_info:
                return instrument_info["InstrumentName"]
            return ""
        except Exception as e:
            logger.error(f"获取股票名称失败: {stock_code}, {str(e)}")
            return ""

    def buy(self, code, price, amount, order_type=ORDER_TYPE_LIMIT):
        if not self.is_available():
            return {"error": self.connection_error_message or "交易系统连接失败"}
            
        # 确定价格类型
        if order_type == 'limit':
            price_type = xtconstant.FIX_PRICE
        else:
            if code.endswith('.SH'):
                price_type = xtconstant.MARKET_SH_CONVERT_5_CANCEL
            else:
                price_type = xtconstant.MARKET_SZ_CONVERT_5_CANCEL
                
        # 如果是市价单，价格可以设为0
        if price_type != xtconstant.FIX_PRICE:
            price = 0
        
        order_id = self.xt_trader.order_stock(
            self.account, code, xtconstant.STOCK_BUY, amount, price_type, price, self._strategy_name, self._remark
        )
        return {"order_id": order_id}

    def sell(self, code, price, amount, order_type=ORDER_TYPE_LIMIT):
        if not self.is_available():
            return {"error": self.connection_error_message or "交易系统连接失败"}
            
        # 确定价格类型
        if order_type == ORDER_TYPE_LIMIT:
            price_type = xtconstant.FIX_PRICE
        else:
            if code.endswith('.SH'):
                price_type = xtconstant.MARKET_SH_CONVERT_5_CANCEL
            else:
                price_type = xtconstant.MARKET_SZ_CONVERT_5_CANCEL
                
        # 如果是市价单，价格可以设为0
        if price_type != xtconstant.FIX_PRICE:
            price = 0
            
        order_id = self.xt_trader.order_stock(
            self.account, code, xtconstant.STOCK_SELL, amount, price_type, price, self._strategy_name, self._remark
        )
        return {"order_id": order_id}

    def cancel(self, order_id):
        if not self.is_available():
            return {"success": False, "message": self.connection_error_message or "交易系统连接失败"}
            
        # 撤单接口需要订单编号
        result = self.xt_trader.cancel_order_stock(self.account, int(order_id))
        return {"success": result == 0, "message": f"撤单结果: {result}"}

    def get_quote(self, code):
        """获取行情数据
        
        Args:
            code: 股票代码
            
        Returns:
            dict: 行情数据，如果获取失败则返回None
        """
        if not self.is_available():
            return None
            
        try:
            quote = self.xt_trader.query_quote(code)
            if quote:
                return {
                    "code": quote.stock_code,
                    "last": quote.last,
                    "open": quote.open,
                    "high": quote.high,
                    "low": quote.low,
                    "ask_price": [quote.ask_price1, quote.ask_price2, quote.ask_price3, quote.ask_price4, quote.ask_price5],
                    "ask_volume": [quote.ask_volume1, quote.ask_volume2, quote.ask_volume3, quote.ask_volume4, quote.ask_volume5],
                    "bid_price": [quote.bid_price1, quote.bid_price2, quote.bid_price3, quote.bid_price4, quote.bid_price5],
                    "bid_volume": [quote.bid_volume1, quote.bid_volume2, quote.bid_volume3, quote.bid_volume4, quote.bid_volume5],
                }
            return None
        except Exception as e:
            logger.error(f"获取行情失败: {code}, {str(e)}")
            return None
    
    def notify_connection_failure(self, message="交易连接断开且重连失败"):
        """通知交易连接失败
        
        Args:
            message: 错误信息
        """
        self.connection_error_message = f"交易系统连接失败：{message}，将在{self.reconnect_interval//60}分钟后自动尝试重连"
        
        # 调用回调通知上层应用
        if self.connect_failed_callback:
            self.connect_failed_callback()
            
        # 发送邮件通知
        trader_info = f"账户：{self._ACCOUNT}，会话ID：{self._SESSION_ID}"
        time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        subject = f"[交易系统] 连接失败通知 - {time_str}"
        body = f"""
交易系统连接失败，请尽快检查！

时间：{time_str}
{trader_info}
错误信息：{message}

系统将在{self.reconnect_interval//60}分钟后自动尝试重连。如需立即恢复，请手动重启交易系统。
        """
        
        MailUtil.send_mail(subject, body)

    def reconnect(self):
        """尝试重新连接交易系统
        
        Returns:
            bool: 重连是否成功
        """
        logger.info("尝试重新连接交易系统")

        # 关闭旧连接
        if self.started:
            logger.info("关闭旧连接")
            self.xt_trader.stop()
            self.started = False
            self.connected = False
            self.subscribed = False
        
        # 尝试范围内的新session_id
        start_id = Config.XT_MIN_SESSION_ID
        end_id = start_id + Config.XT_SESSION_ID_RANGE
        session_id_range = range(start_id, end_id)
        for session_id in random.sample(list(session_id_range), len(session_id_range)):
            self._SESSION_ID = session_id
            self.xt_trader = XtQuantTrader(self._PATH, self._SESSION_ID)
            self._callback = MyXtQuantTraderCallback(self)  # 传入self引用
            self.xt_trader.register_callback(self._callback)
            
            # 重新连接
            self.xt_trader.start()
            self.started = True
            
            result = self.xt_trader.connect()
            if result == 0:
                self.connected = True
                result = self.xt_trader.subscribe(self.account)
                if result == 0:
                    self.subscribed = True
                    logger.info(f"重连成功，使用session_id: {self._SESSION_ID}")
                    
                    # 重置连接失败状态
                    if self.connection_failed:
                        self.connection_failed = False
                        self.connection_error_message = None
                        
                        # 通知连接已恢复
                        time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        subject = f"[交易系统] 连接恢复通知 - {time_str}"
                        body = f"""
交易系统连接已恢复！

时间：{time_str}
账户：{self._ACCOUNT}，会话ID：{self._SESSION_ID}

系统已自动恢复连接，交易功能现已正常。
                        """
                        MailUtil.send_mail(subject, body)
                        
                    return True
                
        logger.error("所有尝试都失败，无法重连")
        return False
    
    def check_reconnect(self):
        """检查是否需要尝试重连
        
        此方法应在主程序循环中定期调用，检查是否需要尝试重连
        """
        if (self.connection_failed and 
            self.last_reconnect_time and 
            (time.time() - self.last_reconnect_time) > self.reconnect_interval):
            
            logger.info("尝试定期重连...")
            if self.reconnect():
                logger.info("定期重连成功")
                self.connection_failed = False
                self.last_reconnect_time = None
            else:
                logger.warning("定期重连失败")
                self.last_reconnect_time = time.time() 

    def handle_order_update(self, order_id, strategy_name):
        """处理订单状态更新，作为中间层避免循环引用
        
        Args:
            order_id: 订单ID
            strategy_name: 策略名称
            
        Returns:
            str: 订单状态，如ORDER_STATUS_COMPLETED、ORDER_STATUS_PARTIAL等，如果处理失败则返回None
        """
        try:
            # 获取position_manager
            position_manager = self.get_position_manager(strategy_name)
            if not position_manager:
                logger.warning(f"获取position_manager失败，无法更新状态: {strategy_name}")
                return None
            
             # 获取订单
            order = self.get_order(order_id)
            if not order:
                logger.warning(f"获取订单失败, 可能下单失败，将订单状态设置为失败: {order_id}")
                position_manager.update_order_status(order_id, 0, ORDER_STATUS_FAILED)
                return None
            
            # 获取订单信息
            order_info = position_manager.get_pending_order(order_id)
            if not order_info:
                logger.warning(f"订单信息不存在，可能已完成或撤单: {order_id}")
                return None
            
            # 获取之前的状态和成交量
            previous_status = order_info.status
            previous_volume = order_info.filled
            
            # 根据委托状态更新订单状态
            if order["order_status"] == xtconstant.ORDER_SUCCEEDED:
                # 全部成交
                filled = order["traded_volume"]
                
                # 如果订单已经标记为完成，则不重复更新
                if previous_status == ORDER_STATUS_COMPLETED:
                    return ORDER_STATUS_COMPLETED
                    
                position_manager.update_order_status(order_id, filled, ORDER_STATUS_COMPLETED)
                
                # 更新持仓（只更新新增的成交部分）
                if filled > previous_volume:
                    new_filled = filled - previous_volume
                    position_manager.update_position(
                        order_info.code,
                        order_info.direction,
                        new_filled,
                    )
                    logger.info(f"订单全部成交: ID={order_id}, 代码={order_info.code}, 价格={order['traded_price']}, 总成交量={filled}, 新增成交量={new_filled}")
                
                return ORDER_STATUS_COMPLETED
                
            elif order["order_status"] == xtconstant.ORDER_PART_SUCC:
                # 部分成交
                filled = order.get("traded_volume", 0)
                position_manager.update_order_status(
                    order_id, filled, ORDER_STATUS_PARTIAL
                )

                # 如果成交量有变化，记录日志并更新持仓
                if filled != previous_volume:
                    target_amount = order_info.amount
                    new_filled = filled - previous_volume
                    logger.info(
                        f"订单部分成交更新: ID={order_id}, 代码={order_info.code}, 目标数量={target_amount}, "
                        f"已成交数量={filled}, 新增成交量={new_filled}, 剩余数量={target_amount - filled}"
                    )

                    # 更新持仓（仅更新新增成交部分）
                    if new_filled > 0:
                        position_manager.update_position(
                            order_info.code,
                            order_info.direction,
                            new_filled,
                        )
                
                return ORDER_STATUS_PARTIAL
                
            elif order["order_status"] in [
                xtconstant.ORDER_CANCELED,
                xtconstant.ORDER_JUNK,
            ]:
                # 已撤单或废单
                # 如果已经标记为取消，则不重复更新
                if previous_status == ORDER_STATUS_CANCELLED:
                    return ORDER_STATUS_CANCELLED
                    
                position_manager.update_order_status(
                    order_id,
                    previous_volume,  # 保留已成交部分
                    ORDER_STATUS_CANCELLED
                )
                
                return ORDER_STATUS_CANCELLED

            elif order["order_status"] in [
                xtconstant.ORDER_UNREPORTED,
                xtconstant.ORDER_WAIT_REPORTING,
                xtconstant.ORDER_REPORTED,
            ]:
                # 未报、待报、已报
                if previous_status != ORDER_STATUS_PENDING:
                    position_manager.update_order_status(order_id, previous_volume, ORDER_STATUS_PENDING)
                
                return ORDER_STATUS_PENDING
            
            return None
            
        except Exception as e:
            logger.error(f"处理订单状态更新异常: order_id={order_id}, error={str(e)}", exc_info=True)
            return None 
        
    def get_strategy_name(self, order_id):
        """获取订单对应的策略名称
        
        Args:
            order_id: 订单ID
            
        Returns:
            str: 策略名称
        """
        for strategy_name, position_manager in self.position_managers.items():
            if position_manager.get_pending_order(order_id):
                return strategy_name
        return None

    def place_market_order_for_remainder(self, order_info:LocalOrder, strategy_name):
        """对未完成的限价单进行市价单补单
        
        当限价单被撤销后，使用此方法下市价单补单，确保交易意图得到执行
        
        Args:
            order_id: 被撤销的限价单ID
            strategy_name: 策略名称
            
        Returns:
            dict: 包含新订单ID和状态信息，如果补单失败则返回错误信息
        """
        try:
            logger.info(f"准备对撤销的限价单进行市价单补单: ID={order_info.order_id}, 策略={strategy_name}")
            
            # 获取position_manager
            position_manager = self.get_position_manager(strategy_name)
            if not position_manager:
                logger.error(f"获取position_manager失败，无法补单: {strategy_name}")
                return {"success": False, "error": "获取position_manager失败"}
            
            # 计算未成交数量
            filled = order_info.filled
            target_amount = order_info.amount
            left_amount = target_amount - filled
            
            # 如果已全部成交，则无需补单
            if left_amount <= 0:
                logger.info(f"无需补单，订单已全部成交: ID={order_info.order_id}")
                return {"success": True, "message": "无需补单，订单已全部成交"}
                
            # 下市价单补单
            code = order_info.code
            direction = order_info.direction
            
            logger.info(f"开始补单: 代码={code}, 方向={direction}, 数量={left_amount}, 类型=market")
            
            if direction == ORDER_DIRECTION_BUY:
                result = self.buy(code, 0, left_amount, ORDER_TYPE_MARKET)
            else:
                result = self.sell(code, 0, left_amount, ORDER_TYPE_MARKET)
                
            new_order_id = result.get("order_id")
            if not new_order_id:
                logger.error(f"市价单补单失败: {result}")
                
            # 添加未完成委托到position_manager
            position_manager.add_pending_order(new_order_id, code, 0, left_amount, direction, ORDER_TYPE_MARKET)
            
            logger.info(f"市价单补单成功: 新订单ID={new_order_id}, 原订单ID={order_info.order_id}, 代码={code}, 方向={direction}, 数量={left_amount}")
            
            return {"success": True, "order_id": new_order_id}
            
        except Exception as e:
            logger.error(f"市价单补单过程发生异常: {str(e)}", exc_info=True)
            return {"success": False, "error": f"市价单补单异常: {str(e)}"}