#!/usr/bin/env python3
"""
GodEye Signaling Server
Сервер для обмена SDP и ICE кандидатами между устройством и оператором
"""

import asyncio
import websockets
import json
import logging
from datetime import datetime
import uuid

# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class SignalingServer:
    def __init__(self):
        self.clients = {}  # client_id -> websocket
        self.sessions = {}  # session_id -> {device: ws, operator: ws}

    async def register_client(self, websocket, path):
        """Регистрация нового клиента"""
        client_id = str(uuid.uuid4())
        self.clients[client_id] = websocket

        logger.info(f"🔗 Клиент подключен: {client_id}")

        try:
            # Отправляем ID клиенту
            await websocket.send(json.dumps({
                "type": "client_registered",
                "client_id": client_id,
                "timestamp": datetime.now().isoformat()
            }))

            async for message in websocket:
                await self.handle_message(client_id, message)

        except websockets.exceptions.ConnectionClosed:
            logger.info(f"🔌 Клиент отключен: {client_id}")
        finally:
            # Очистка при отключении
            await self.cleanup_client(client_id)

    async def handle_message(self, client_id, message):
        """Обработка сообщений от клиентов"""
        try:
            data = json.loads(message)
            message_type = data.get("type")

            logger.info(f"📨 Сообщение от {client_id}: {message_type}")

            if message_type == "create_session":
                await self.create_session(client_id, data)
            elif message_type == "join_session":
                await self.join_session(client_id, data)
            elif message_type == "offer":
                await self.relay_offer(client_id, data)
            elif message_type == "answer":
                await self.relay_answer(client_id, data)
            elif message_type == "ice_candidate":
                await self.relay_ice_candidate(client_id, data)
            elif message_type == "hangup":
                await self.handle_hangup(client_id, data)
            else:
                logger.warning(f"⚠️ Неизвестный тип сообщения: {message_type}")

        except json.JSONDecodeError:
            logger.error(f"❌ Ошибка парсинга JSON от {client_id}")
        except Exception as e:
            logger.error(f"❌ Ошибка обработки сообщения: {e}")

    async def create_session(self, client_id, data):
        """Создание новой сессии (устройство)"""
        session_id = str(uuid.uuid4())
        device_info = data.get("device_info", {})

        self.sessions[session_id] = {
            "device": self.clients[client_id],
            "operator": None,
            "device_info": device_info,
            "created_at": datetime.now().isoformat()
        }

        # Отправляем подтверждение устройству
        await self.clients[client_id].send(json.dumps({
            "type": "session_created",
            "session_id": session_id,
            "timestamp": datetime.now().isoformat()
        }))

        logger.info(f"📱 Сессия создана: {session_id} для устройства {device_info.get('model', 'Unknown')}")

    async def join_session(self, client_id, data):
        """Подключение к сессии (оператор)"""
        session_id = data.get("session_id")

        if session_id not in self.sessions:
            await self.clients[client_id].send(json.dumps({
                "type": "error",
                "message": "Session not found"
            }))
            return

        session = self.sessions[session_id]
        if session["operator"] is not None:
            await self.clients[client_id].send(json.dumps({
                "type": "error",
                "message": "Session already has an operator"
            }))
            return

        # Подключаем оператора
        session["operator"] = self.clients[client_id]

        # Уведомляем оператора
        await self.clients[client_id].send(json.dumps({
            "type": "session_joined",
            "session_id": session_id,
            "device_info": session["device_info"],
            "timestamp": datetime.now().isoformat()
        }))

        # Уведомляем устройство
        await session["device"].send(json.dumps({
            "type": "operator_joined",
            "session_id": session_id,
            "timestamp": datetime.now().isoformat()
        }))

        logger.info(f"👤 Оператор подключен к сессии: {session_id}")

    async def relay_offer(self, client_id, data):
        """Передача SDP offer"""
        session_id = data.get("session_id")
        session = self.sessions.get(session_id)

        if not session or session["operator"] is None:
            return

        # Передаем offer оператору
        await session["operator"].send(json.dumps({
            "type": "offer",
            "session_id": session_id,
            "sdp": data.get("sdp"),
            "timestamp": datetime.now().isoformat()
        }))

        logger.info(f"📞 SDP Offer передан в сессии: {session_id}")

    async def relay_answer(self, client_id, data):
        """Передача SDP answer"""
        session_id = data.get("session_id")
        session = self.sessions.get(session_id)

        if not session:
            return

        # Передаем answer устройству
        await session["device"].send(json.dumps({
            "type": "answer",
            "session_id": session_id,
            "sdp": data.get("sdp"),
            "timestamp": datetime.now().isoformat()
        }))

        logger.info(f"📞 SDP Answer передан в сессии: {session_id}")

    async def relay_ice_candidate(self, client_id, data):
        """Передача ICE кандидатов"""
        session_id = data.get("session_id")
        session = self.sessions.get(session_id)

        if not session:
            return

        # Определяем кому передавать (устройству или оператору)
        if session["device"] == self.clients[client_id]:
            # От устройства к оператору
            if session["operator"]:
                await session["operator"].send(json.dumps({
                    "type": "ice_candidate",
                    "session_id": session_id,
                    "candidate": data.get("candidate"),
                    "timestamp": datetime.now().isoformat()
                }))
        else:
            # От оператора к устройству
            await session["device"].send(json.dumps({
                "type": "ice_candidate",
                "session_id": session_id,
                "candidate": data.get("candidate"),
                "timestamp": datetime.now().isoformat()
            }))

        logger.info(f"🧊 ICE candidate передан в сессии: {session_id}")

    async def handle_hangup(self, client_id, data):
        """Завершение сессии"""
        session_id = data.get("session_id")
        session = self.sessions.get(session_id)

        if not session:
            return

        # Уведомляем обе стороны
        if session["device"]:
            await session["device"].send(json.dumps({
                "type": "hangup",
                "session_id": session_id,
                "timestamp": datetime.now().isoformat()
            }))

        if session["operator"]:
            await session["operator"].send(json.dumps({
                "type": "hangup",
                "session_id": session_id,
                "timestamp": datetime.now().isoformat()
            }))

        # Удаляем сессию
        del self.sessions[session_id]
        logger.info(f"📴 Сессия завершена: {session_id}")

    async def cleanup_client(self, client_id):
        """Очистка при отключении клиента"""
        if client_id in self.clients:
            del self.clients[client_id]

        # Удаляем сессии с этим клиентом
        sessions_to_remove = []
        for session_id, session in self.sessions.items():
            if (session["device"] == self.clients.get(client_id) or
                session["operator"] == self.clients.get(client_id)):
                sessions_to_remove.append(session_id)

        for session_id in sessions_to_remove:
            del self.sessions[session_id]
            logger.info(f"🧹 Сессия {session_id} удалена из-за отключения клиента")

def main():
    """Запуск сигналинг сервера"""
    server = SignalingServer()

    print("🚀 GodEye Signaling Server")
    print("==========================")
    print("📡 Сервер запущен на ws://localhost:8765")
    print("💡 Для остановки нажмите Ctrl+C")
    print()

    # Исправляем проблему с event loop
    async def run_server():
        start_server = websockets.serve(
            server.register_client,
            "0.0.0.0",  # Слушаем на всех интерфейсах
            8765
        )
        await start_server
        await asyncio.Future()  # Запускаем вечно

    try:
        asyncio.run(run_server())
    except KeyboardInterrupt:
        print("\n🛑 Сервер остановлен")

if __name__ == "__main__":
    main()
