import asyncio
import json
import logging
import uuid
import time
import base64
import hashlib
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import uvicorn
from typing import List, Dict, Any, Optional

from .profile import local_profile
from .chat_store import RoomChatStore
from .project_store import ProjectStore
from .project_sync import apply_incoming_file

from runtime.security.moderation import ModerationManager
from runtime.security.content_scanner import ContentScanner
from runtime.security.permissions import PermissionManager, Action
from runtime.security.trust import TrustManager, TrustLevel
from runtime.security.docker_sandbox import DockerSandbox, SandboxConfig

logger = logging.getLogger("brynq.rooms.host")

class RoomHost:
    def __init__(self, manager):
        self.manager = manager
        self.app = FastAPI(title="Room P2P Host Server")
        self.active_connections: List[WebSocket] = []
        # Persistent chat history — survives host restarts
        room_id = (manager.active_room or {}).get("room_id", "default")
        self.chat_store = RoomChatStore(room_id)

        # New: Project Store
        self.project_store: Optional[ProjectStore] = None
        self.active_project_id: Optional[str] = None

        # Security layer
        prof = local_profile.get()
        self.moderation = ModerationManager()
        self.content_scanner = ContentScanner()
        self.permissions = PermissionManager()
        self.trust = TrustManager(owner_id=prof.get("user_id", ""))
        self.sandbox = DockerSandbox()

        self.server = None
        self._setup_routes()

    def _setup_routes(self):
        @self.app.websocket("/ws/room")
        async def websocket_endpoint(ws: WebSocket):
            await ws.accept()
            self.active_connections.append(ws)
            # Send persisted history on connect
            history = self.chat_store.get_history()
            await ws.send_json({"type": "history", "messages": history})

            # New: Send project manifest if active
            if self.project_store and self.active_project_id:
                manifest = self.project_store.get_manifest()
                await ws.send_json({
                    "type": "project_sync",
                    "project_id": self.active_project_id,
                    "manifest": manifest
                })

            # Announce join
            await self.broadcast({"type": "system", "content": "A peer has connected."})

            try:
                while True:
                    data = await ws.receive_text()
                    msg = json.loads(data)
                    msg_type = msg.get("type")
                    sender_id = msg.get("sender_id", "")

                    # --- Security: block check ---
                    if sender_id and self.moderation.is_blocked(sender_id):
                        logger.info("Dropped message from blocked user %s", sender_id)
                        continue

                    if msg_type == "chat":
                        msg["id"] = str(uuid.uuid4())
                        msg["timestamp"] = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
                        self.chat_store.add_message(msg)
                        await self.broadcast(msg)
                    elif msg_type == "task_event":
                        # --- Security: content scan + trust + permission + sandbox ---
                        task_content = msg.get("code", msg.get("content", ""))
                        scan = self.content_scanner.scan(task_content)
                        if not scan.safe:
                            await ws.send_json({
                                "type": "task_rejected",
                                "reason": scan.reason,
                            })
                            continue

                        cap = self.trust.can_perform(sender_id, "run_code")
                        if not cap["allowed"]:
                            await ws.send_json({
                                "type": "task_rejected",
                                "reason": "Insufficient trust level to run code.",
                            })
                            continue

                        if cap["prompted"]:
                            sender_name = msg.get("sender_name", sender_id)
                            if not self.permissions.has_permission(sender_id, Action.RUN_CODE.value):
                                await ws.send_json({
                                    "type": "permission_required",
                                    "action": "run_code",
                                    "peer_id": sender_id,
                                    "peer_name": sender_name,
                                })
                                continue

                        if cap["sandboxed"] and self.sandbox.available:
                            result = self.sandbox.run(task_content)
                            await ws.send_json({
                                "type": "task_result",
                                "success": result.success,
                                "stdout": result.stdout,
                                "stderr": result.stderr,
                                "sandboxed": True,
                            })
                            continue
                        elif cap["sandboxed"] and not self.sandbox.available:
                            await ws.send_json({
                                "type": "task_rejected",
                                "reason": "Docker not available. Remote code execution requires a sandbox.",
                            })
                            continue

                        await self.broadcast(msg)
                    
                    # New: Project Message Types
                    elif msg_type == "project_sync_request":
                        # Client wants the current file manifest
                        if self.project_store:
                            manifest = self.project_store.get_manifest()
                            await ws.send_json({
                                "type": "project_sync",
                                "project_id": self.active_project_id,
                                "manifest": manifest
                            })

                    elif msg_type == "project_file_request":
                        # Client wants a specific file
                        if self.project_store:
                            rel_path = msg.get("relative_path", "")
                            content = self.project_store.read_file(rel_path)
                            if content is not None:
                                await ws.send_json({
                                    "type": "project_file_response",
                                    "project_id": self.active_project_id,
                                    "relative_path": rel_path,
                                    "content_b64": base64.b64encode(content).decode("ascii"),
                                    "content_hash": hashlib.sha256(content).hexdigest(),
                                    "size_bytes": len(content)
                                })

                    elif msg_type == "project_file_push":
                        # --- Security: trust check for file writes ---
                        cap = self.trust.can_perform(sender_id, "write_file")
                        if not cap["allowed"]:
                            await ws.send_json({
                                "type": "file_rejected",
                                "reason": "Insufficient trust level to write files.",
                            })
                            continue
                        if cap["prompted"]:
                            if not self.permissions.has_permission(sender_id, Action.WRITE_FILE.value):
                                sender_name = msg.get("sender_name", sender_id)
                                await ws.send_json({
                                    "type": "permission_required",
                                    "action": "write_file",
                                    "peer_id": sender_id,
                                    "peer_name": sender_name,
                                })
                                continue

                        # Content scan the file being pushed
                        file_content = msg.get("content_b64", "")
                        if file_content:
                            try:
                                decoded = base64.b64decode(file_content).decode("utf-8", errors="replace")
                                scan = self.content_scanner.scan(decoded)
                                if not scan.safe:
                                    await ws.send_json({
                                        "type": "file_rejected",
                                        "reason": f"File content flagged: {scan.reason}",
                                    })
                                    continue
                            except Exception:
                                pass  # binary file, skip scan

                        if self.project_store:
                            apply_incoming_file(self.project_store, msg)
                        await self.broadcast(msg)

                    elif msg_type == "project_file_deleted":
                        # Someone deleted a file — apply locally and broadcast
                        if self.project_store:
                            self.project_store.delete_file(
                                msg.get("relative_path", ""),
                                user_id=msg.get("user_id", ""),
                                user_name=msg.get("user_name", "")
                            )
                        await self.broadcast(msg)
                    
                    # New: A2A Protocol Message Types
                    elif msg_type in ("agent_status", "agent_chat", "agent_task", "agent_result"):
                        self.manager.a2a.handle_incoming_message(msg)
                        await self.broadcast(msg)
                        
            except WebSocketDisconnect:
                self.active_connections.remove(ws)
                await self.broadcast({"type": "system", "content": "A peer disconnected."})

    async def broadcast(self, message: Dict[str, Any]):
        for connection in self.active_connections:
            try:
                await connection.send_json(message)
            except Exception:
                pass

    async def broadcast_chat(self, content: str):
        prof = local_profile.get()
        msg = {
            "type": "chat",
            "content": content,
            "sender_id": prof["user_id"],
            "sender_name": prof["username"],
            "id": str(uuid.uuid4()),
            "timestamp": time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
        }
        self.chat_store.add_message(msg)
        await self.broadcast(msg)

    async def start(self):
        config = uvicorn.Config(app=self.app, host="0.0.0.0", port=8081, log_level="warning")
        self.server = uvicorn.Server(config)
        logger.info("Starting RoomHost WS Server on port 8081...")
        await self.server.serve()

    async def stop(self):
        if self.server:
            self.server.should_exit = True
        if self.chat_store:
            self.chat_store.close()
