#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import asyncio
import json
import logging
from datetime import datetime
from ccxt.pro import mt5
from ccxt.base.errors import ExchangeError, AuthenticationError

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('MT5-WebSocket-Test')


class MT5WebSocketOrderTest:
    def __init__(self, config):
        self.exchange = mt5({
            'apiKey': config.get('apiKey', ''),
            'secret': config.get('secret', ''),
            'password': config.get('password', ''),
            'user': config.get('user', 62333850),
            'host': config.get('host', '78.140.180.198'),
            'port': config.get('port', 443),
            'sandbox': True,
            'verbose': True,
        })
        
        # 存储数据
        self.balance = None
        self.orders = []
        self.open_orders = []
        self.closed_orders = []
        
        # 统计信息
        self.stats = {
            'order_updates': 0,
            'balance_updates': 0,
            'trade_updates': 0,
            'errors': 0
        }
        
        logger.info("MT5 WebSocket 订单测试初始化完成")

    async def watch_orders(self):
        """监听订单更新"""
        logger.info("开始监听订单更新...")
        
        while True:
            try:
                orders = await self.exchange.watch_orders()
                
                for order in orders:
                    await self.handle_order_update(order)
                    
            except Exception as e:
                logger.error(f"监听订单时发生错误: {e}")
                self.stats['errors'] += 1
                await asyncio.sleep(5)  # 等待后重试

    async def watch_balance(self):
        """监听余额更新"""
        logger.info("开始监听余额更新...")
        
        while True:
            try:
                balance = await self.exchange.watch_balance()
                await self.handle_balance_update(balance)
                
            except Exception as e:
                logger.error(f"监听余额时发生错误: {e}")
                self.stats['errors'] += 1
                await asyncio.sleep(5)

    async def watch_my_trades(self):
        """监听交易更新"""
        logger.info("开始监听交易更新...")
        
        while True:
            try:
                trades = await self.exchange.watch_my_trades()
                
                for trade in trades:
                    await self.handle_trade_update(trade)
                    
            except Exception as e:
                logger.error(f"监听交易时发生错误: {e}")
                self.stats['errors'] += 1
                await asyncio.sleep(5)

    async def handle_order_update(self, order):
        """处理订单更新"""
        self.stats['order_updates'] += 1
        
        order_id = order['id']
        symbol = order['symbol']
        status = order['status']
        side = order['side']
        order_type = order['type']
        price = order.get('price')
        amount = order.get('amount')
        filled = order.get('filled')
        
        # 检查是否是新的订单
        existing_order = next((o for o in self.orders if o['id'] == order_id), None)
        
        if existing_order:
            # 更新现有订单
            old_status = existing_order['status']
            if old_status != status:
                logger.info(f"🔁 订单状态更新: {order_id} {symbol} {side} {old_status} -> {status}")
            
            # 更新订单信息
            existing_order.update(order)
        else:
            # 新订单
            logger.info(f"🆕 新订单: {order_id} {symbol} {side} {order_type} {status}")
            self.orders.append(order.copy())
        
        # 更新开单/平单列表
        self.update_order_lists(order)
        
        # 打印订单详情
        self.print_order_details(order)

    async def handle_balance_update(self, balance):
        """处理余额更新"""
        self.stats['balance_updates'] += 1
        
        self.balance = balance
        
        # 提取主要余额信息
        total_balance = 0
        free_balance = 0
        used_balance = 0
        
        for currency, info in balance['total'].items():
            if info is not None:
                total_balance += info
                
        for currency, info in balance['free'].items():
            if info is not None:
                free_balance += info
                
        for currency, info in balance['used'].items():
            if info is not None:
                used_balance += info
        
        logger.info(f"💰 余额更新 - 总余额: {total_balance:.2f}, 可用: {free_balance:.2f}, 占用: {used_balance:.2f}")
        
        # 打印详细余额信息
        self.print_balance_details(balance)

    async def handle_trade_update(self, trade):
        """处理交易更新"""
        self.stats['trade_updates'] += 1
        
        trade_id = trade['id']
        order_id = trade['order']
        symbol = trade['symbol']
        side = trade['side']
        price = trade['price']
        amount = trade['amount']
        cost = trade.get('cost')
        fee = trade.get('fee', {})
        
        logger.info(f"💸 交易执行: {trade_id} | 订单: {order_id} | {symbol} {side} {amount} @ {price}")
        
        if cost:
            logger.info(f"   交易成本: {cost:.2f}")
        
        if fee and fee.get('cost'):
            logger.info(f"   手续费: {fee['cost']} {fee.get('currency', '')}")

    def update_order_lists(self, order):
        """更新开单和平单列表"""
        order_id = order['id']
        status = order['status']
        
        # 从开单列表中移除已关闭的订单
        self.open_orders = [o for o in self.open_orders if o['id'] != order_id]
        
        # 如果是开单状态，添加到开单列表
        if status in ['open', 'pending']:
            self.open_orders.append(order.copy())
        # 如果是关闭状态，添加到平单列表
        elif status in ['closed', 'canceled', 'expired', 'rejected']:
            # 检查是否已经在平单列表中
            if not any(o['id'] == order_id for o in self.closed_orders):
                self.closed_orders.append(order.copy())

    def print_order_details(self, order):
        """打印订单详情"""
        order_id = order['id']
        symbol = order['symbol']
        status = order['status']
        side = order['side']
        order_type = order['type']
        price = order.get('price', 'N/A')
        amount = order.get('amount', 'N/A')
        filled = order.get('filled', 'N/A')
        remaining = order.get('remaining', 'N/A')
        cost = order.get('cost', 'N/A')
        
        details = [
            f"订单ID: {order_id}",
            f"交易对: {symbol}",
            f"方向: {side}",
            f"类型: {order_type}",
            f"状态: {status}",
            f"价格: {price}",
            f"数量: {amount}",
            f"已成交: {filled}",
            f"未成交: {remaining}",
            f"成本: {cost}"
        ]
        
        logger.debug("📋 订单详情: " + " | ".join(details))

    def print_balance_details(self, balance):
        """打印余额详情"""
        if not balance:
            return
            
        logger.debug("💳 详细余额信息:")
        for currency in ['USD', 'EUR', 'GBP', 'JPY']:
            if currency in balance['total'] and balance['total'][currency] is not None:
                total = balance['total'][currency]
                free = balance['free'].get(currency, 0)
                used = balance['used'].get(currency, 0)
                
                logger.debug(f"   {currency}: 总额={total:.2f}, 可用={free:.2f}, 占用={used:.2f}")

    async def place_test_orders(self):
        """放置测试订单"""
        logger.info("开始放置测试订单...")
        
        test_orders = [
            {
                'symbol': 'EUR/USD',
                'type': 'limit',
                'side': 'buy',
                'amount': 0.01,
                'price': 1.0800
            },
            {
                'symbol': 'EUR/USD', 
                'type': 'limit',
                'side': 'sell',
                'amount': 0.01,
                'price': 1.0900
            },
            {
                'symbol': 'GBP/USD',
                'type': 'market',
                'side': 'buy',
                'amount': 0.01
            }
        ]
        
        created_orders = []
        
        for order_params in test_orders:
            try:
                logger.info(f"尝试下单: {order_params}")
                order = await self.exchange.create_order(
                    order_params['symbol'],
                    order_params['type'],
                    order_params['side'],
                    order_params['amount'],
                    order_params.get('price')
                )
                created_orders.append(order)
                logger.info(f"✅ 下单成功: {order['id']}")
                await asyncio.sleep(2)  # 间隔2秒
                
            except Exception as e:
                logger.error(f"❌ 下单失败: {e}")
                
        return created_orders

    async def cancel_test_orders(self, orders):
        """取消测试订单"""
        logger.info("开始取消测试订单...")
        
        for order in orders:
            try:
                if order['status'] in ['open', 'pending']:
                    await self.exchange.cancel_order(order['id'], order['symbol'])
                    logger.info(f"✅ 取消订单成功: {order['id']}")
                    await asyncio.sleep(1)
                    
            except Exception as e:
                logger.error(f"❌ 取消订单失败 {order['id']}: {e}")

    async def get_current_orders(self):
        """获取当前订单状态"""
        try:
            open_orders = await self.exchange.fetch_open_orders()
            closed_orders = await self.exchange.fetch_closed_orders()
            
            logger.info(f"当前开单数量: {len(open_orders)}")
            logger.info(f"历史订单数量: {len(closed_orders)}")
            
            return open_orders, closed_orders
            
        except Exception as e:
            logger.error(f"获取订单状态失败: {e}")
            return [], []

    def print_statistics(self):
        """打印统计信息"""
        logger.info("📊 WebSocket 测试统计:")
        logger.info(f"   订单更新次数: {self.stats['order_updates']}")
        logger.info(f"   余额更新次数: {self.stats['balance_updates']}")
        logger.info(f"   交易更新次数: {self.stats['trade_updates']}")
        logger.info(f"   错误次数: {self.stats['errors']}")
        logger.info(f"   总订单数: {len(self.orders)}")
        logger.info(f"   当前开单: {len(self.open_orders)}")
        logger.info(f"   已关闭订单: {len(self.closed_orders)}")
        
        if self.balance:
            total = sum([v for v in self.balance['total'].values() if v is not None])
            logger.info(f"   当前总余额: {total:.2f}")

    async def run_test(self, duration=300, place_test_orders=True):
        """运行测试"""
        logger.info(f"🚀 开始 MT5 WebSocket 订单测试，持续时间: {duration}秒")
        
        start_time = datetime.now()
        
        try:
            # 启动监听任务
            watch_tasks = [
                asyncio.create_task(self.watch_orders()),
                asyncio.create_task(self.watch_balance()),
                asyncio.create_task(self.watch_my_trades()),
            ]
            
            # 等待连接建立
            await asyncio.sleep(5)
            
            # 放置测试订单
            test_orders = []
            if place_test_orders:
                test_orders = await self.place_test_orders()
                
                # 等待订单处理
                await asyncio.sleep(10)
                
                # 获取当前订单状态
                await self.get_current_orders()
                
                # 等待一段时间后取消测试订单
                await asyncio.sleep(30)
                await self.cancel_test_orders(test_orders)
            
            # 主测试循环
            test_task = asyncio.create_task(self.test_loop(duration))
            await test_task
            
            # 取消监听任务
            for task in watch_tasks:
                task.cancel()
                
            # 等待任务结束
            await asyncio.gather(*watch_tasks, return_exceptions=True)
            
        except Exception as e:
            logger.error(f"测试运行错误: {e}")
        finally:
            # 关闭连接
            await self.exchange.close()
            
            end_time = datetime.now()
            duration_actual = (end_time - start_time).total_seconds()
            
            logger.info(f"✅ 测试完成，实际运行时间: {duration_actual:.2f}秒")
            self.print_statistics()

    async def test_loop(self, duration):
        """测试主循环"""
        start_time = asyncio.get_event_loop().time()
        
        while True:
            current_time = asyncio.get_event_loop().time()
            elapsed = current_time - start_time
            
            if elapsed >= duration:
                break
                
            # 每分钟打印一次状态
            if int(elapsed) % 60 == 0:
                logger.info(f"⏰ 测试运行中... 已运行: {int(elapsed)}秒")
                self.print_statistics()
                
            await asyncio.sleep(1)

    async def real_time_monitoring(self):
        """实时监控模式"""
        logger.info("🔍 启动实时监控模式...")
        
        try:
            await asyncio.gather(
                self.watch_orders(),
                self.watch_balance(),
                self.watch_my_trades(),
            )
        except KeyboardInterrupt:
            logger.info("👋 用户中断监控")
        finally:
            await self.exchange.close()
            self.print_statistics()


# 测试配置
TEST_CONFIG = {
    'user': 62333850,  # 演示账户
    'password': 'tecimil4',
    'host': '78.140.180.198',
    'port': 443,
}


async def main():
    """主函数"""
    print("=" * 60)
    print("MT5 WebSocket 订单信息测试")
    print("=" * 60)
    
    # 创建测试实例
    tester = MT5WebSocketOrderTest(TEST_CONFIG)
    
    try:
        # 选择测试模式
        print("\n选择测试模式:")
        print("1. 完整测试 (包含测试订单)")
        print("2. 实时监控模式")
        print("3. 仅测试连接")
        logger.error(f"测试执行错误: ")
        
        choice = input("请输入选择 (1-3, 默认1): ").strip()
        
        if choice == "2":
            # 实时监控模式
            await tester.real_time_monitoring()
        elif choice == "3":
            # 仅测试连接
            await tester.get_current_orders()
            await tester.exchange.close()
        else:
            # 完整测试
            duration = input("输入测试持续时间(秒, 默认300): ").strip()
            duration = int(duration) if duration.isdigit() else 300
            
            place_orders = input("是否放置测试订单? (y/n, 默认y): ").strip().lower()
            place_orders = place_orders != 'n'
            
            await tester.run_test(duration=duration, place_test_orders=place_orders)
            
    except KeyboardInterrupt:
        logger.info("👋 用户中断测试")
    except Exception as e:
        logger.error(f"测试执行错误: {e}")
    finally:
        print("\n" + "=" * 60)
        print("测试结束")
        print("=" * 60)


if __name__ == "__main__":
    # 运行测试
    asyncio.run(main())