#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PPT遥控器Web服务端
部署在云服务器上，作为手机APP和电脑服务端之间的中转
"""

import asyncio
import websockets
import json
import uuid
import time
from datetime import datetime

# 存储活跃的电脑服务端连接
# key: 临时地址ID, value: {"websocket": websocket连接, "client_info": 客户端信息, "created_at": 创建时间}
active_servers = {}

# 存储手机APP连接
# key: 临时地址ID, value: websocket连接
mobile_connections = {}

# 验证密钥（实际部署时应该从环境变量或配置文件读取）
VERIFICATION_KEY = "ppt_remote_secret_key"

async def handle_server_connection(websocket, path):
    """处理电脑服务端的连接"""
    server_id = None
    try:
        # 验证阶段
        auth_data = await websocket.recv()
        auth_info = json.loads(auth_data)
        
        if auth_info.get("type") != "auth" or auth_info.get("key") != VERIFICATION_KEY:
            await websocket.send(json.dumps({"type": "error", "message": "验证失败"}))
            return
        
        # 生成临时地址ID
        server_id = str(uuid.uuid4())[:8]
        client_info = auth_info.get("client_info", {})
        
        # 存储连接
        active_servers[server_id] = {
            "websocket": websocket,
            "client_info": client_info,
            "created_at": datetime.now().timestamp()
        }
        
        # 返回临时地址
        await websocket.send(json.dumps({
            "type": "auth_success",
            "server_id": server_id,
            "message": "验证成功，已分配临时地址"
        }))
        
        print(f"电脑服务端已连接，临时地址: {server_id}")
        
        # 处理来自电脑服务端的消息
        async for message in websocket:
            try:
                data = json.loads(message)
                # 这里可以处理电脑服务端的消息
                print(f"收到电脑服务端消息: {data}")
            except json.JSONDecodeError:
                print(f"无效的JSON消息: {message}")
                
    except websockets.exceptions.ConnectionClosedError:
        print(f"电脑服务端连接已关闭: {server_id}")
    finally:
        # 清理连接
        if server_id and server_id in active_servers:
            del active_servers[server_id]
            # 同时清理对应的手机连接
            if server_id in mobile_connections:
                try:
                    await mobile_connections[server_id].close()
                except:
                    pass
                del mobile_connections[server_id]
            print(f"已清理电脑服务端资源: {server_id}")

async def handle_mobile_connection(websocket, path):
    """处理手机APP的连接"""
    server_id = None
    try:
        # 解析路径获取临时地址
        if path and len(path) > 1:
            server_id = path[1:]
        else:
            await websocket.send(json.dumps({"type": "error", "message": "无效的连接地址"}))
            return
        
        # 检查临时地址是否存在
        if server_id not in active_servers:
            await websocket.send(json.dumps({"type": "error", "message": "服务端不存在或已关闭"}))
            return
        
        # 存储手机连接
        mobile_connections[server_id] = websocket
        
        # 发送连接成功消息
        await websocket.send(json.dumps({
            "type": "connect_success",
            "message": "连接成功，可以开始控制PPT"
        }))
        
        print(f"手机APP已连接到临时地址: {server_id}")
        
        # 处理来自手机APP的控制命令
        async for message in websocket:
            try:
                data = json.loads(message)
                # 转发命令到电脑服务端
                if server_id in active_servers:
                    await active_servers[server_id]["websocket"].send(message)
                    print(f"转发控制命令到电脑服务端 {server_id}: {data}")
                else:
                    await websocket.send(json.dumps({"type": "error", "message": "服务端已关闭"}))
                    break
            except json.JSONDecodeError:
                print(f"无效的JSON消息: {message}")
                
    except websockets.exceptions.ConnectionClosedError:
        print(f"手机APP连接已关闭: {server_id}")
    finally:
        # 清理连接
        if server_id and server_id in mobile_connections:
            del mobile_connections[server_id]
            print(f"已清理手机APP资源: {server_id}")

async def cleanup_expired_servers():
    """清理过期的服务端连接"""
    while True:
        current_time = datetime.now().timestamp()
        expired_servers = []
        
        for server_id, server_info in active_servers.items():
            # 清理超过24小时的连接
            if current_time - server_info["created_at"] > 24 * 3600:
                expired_servers.append(server_id)
        
        for server_id in expired_servers:
            print(f"清理过期服务端: {server_id}")
            if server_id in active_servers:
                try:
                    await active_servers[server_id]["websocket"].close()
                except:
                    pass
                del active_servers[server_id]
            if server_id in mobile_connections:
                try:
                    await mobile_connections[server_id].close()
                except:
                    pass
                del mobile_connections[server_id]
        
        await asyncio.sleep(3600)  # 每小时检查一次

async def main():
    """主函数"""
    # 启动清理任务
    asyncio.create_task(cleanup_expired_servers())
    
    # 启动WebSocket服务器
    server = await websockets.serve(
        handle_server_connection,
        "0.0.0.0",
        8766,
        ping_interval=30,
        ping_timeout=60
    )
    
    # 启动手机APP连接服务器
    mobile_server = await websockets.serve(
        handle_mobile_connection,
        "0.0.0.0",
        8767,
        ping_interval=30,
        ping_timeout=60
    )
    
    print("Web服务端已启动")
    print("电脑服务端连接端口: 8766")
    print("手机APP连接端口: 8767")
    
    await server.wait_closed()
    await mobile_server.wait_closed()

if __name__ == "__main__":
    asyncio.run(main())
