import asyncio
import json
import logging
import websockets
import base64
from typing import Dict, Any, Optional

from .profile import local_profile
from .project_store import ProjectStore
from .project_sync import apply_incoming_file
from runtime.security.moderation import ModerationManager

import time
import uuid

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

class RoomClient:
    def __init__(self, manager, host_ws_url: str, room_id: str):
        self.manager = manager
        self.host_ws_url = host_ws_url
        self.room_id = room_id
        self.ws = None
        self._connected = False
        self.project_store: Optional[ProjectStore] = None
        self.active_project_id: Optional[str] = None
        self.moderation = ModerationManager()

    async def connect(self):
        try:
            # Try connecting directly to host first
            self.ws = await websockets.connect(self.host_ws_url)
            self._connected = True
            logger.info(f"Connected to room host at {self.host_ws_url}")
            asyncio.create_task(self.listen())
        except Exception as e:
            logger.warning(f"Direct connection failed, falling back to cloud relay: {e}")
            # Cloud WebSocket fallback (MVP URL)
            self.ws = await websockets.connect(f"ws://localhost:8000/ws/signal/{self.room_id}")
            self._connected = True
            asyncio.create_task(self.listen())

    async def listen(self):
        try:
            while self._connected:
                msg = await self.ws.recv()
                data = json.loads(msg)
                msg_type = data.get("type")
                sender_id = data.get("sender_id", "")

                # --- Security: drop messages from blocked users ---
                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":
                    logger.info(f"Room Chat: {data.get('sender_name')}: {data.get('content')}")
                elif msg_type == "system":
                    logger.info(f"Room System: {data.get('content')}")
                elif msg_type == "history":
                    pass # ignore for CLI simplistic MVP
                
                # New: Project Message Types
                elif msg_type == "project_sync":
                    # Received manifest from host — diff and request missing files
                    await self._handle_project_sync(data)

                elif msg_type == "project_file_response":
                    # Received a requested file from host
                    self._handle_file_response(data)

                elif msg_type == "project_file_push":
                    # Another user pushed a file change
                    if self.project_store:
                        apply_incoming_file(self.project_store, data)

                elif msg_type == "project_file_deleted":
                    if self.project_store:
                        self.project_store.delete_file(
                            data.get("relative_path", ""),
                            user_id=data.get("user_id", ""),
                            user_name=data.get("user_name", "")
                        )

                elif msg_type == "project_created":
                    # Host created a project — initialize local store
                    self._handle_project_created(data)
                
                # New: A2A Protocol Message Types
                elif msg_type in ("agent_status", "agent_chat", "agent_task", "agent_result"):
                    self.manager.a2a.handle_incoming_message(data)
                    
        except websockets.exceptions.ConnectionClosed:
            logger.info("Disconnected from room.")
            self._connected = False

    async def _handle_project_sync(self, data: dict):
        """Diff manifest against local state and request missing/stale files."""
        project_id = data.get("project_id", "")
        manifest = data.get("manifest", [])

        if not self.project_store or self.active_project_id != project_id:
            # Initialize store for this project
            self.active_project_id = project_id
            self.project_store = ProjectStore(project_id)

        local_manifest = {f["relative_path"]: f["content_hash"]
                          for f in self.project_store.get_manifest()}

        for entry in manifest:
            rel_path = entry["relative_path"]
            remote_hash = entry["content_hash"]

            if local_manifest.get(rel_path) != remote_hash:
                # Need this file — request it
                await self.ws.send(json.dumps({
                    "type": "project_file_request",
                    "project_id": project_id,
                    "relative_path": rel_path
                }))

    def _handle_file_response(self, data: dict):
        """Write a received file to local project store."""
        if not self.project_store:
            return
        try:
            content = base64.b64decode(data.get("content_b64", ""))
            self.project_store.add_file(
                relative_path=data.get("relative_path", ""),
                content=content,
                user_id="sync",
                user_name="sync"
            )
        except Exception as e:
            logger.error(f"Failed to handle file response: {e}")

    def _handle_project_created(self, data: dict):
        """Handle project creation announcement."""
        project_id = data.get("project_id")
        if project_id:
            self.active_project_id = project_id
            self.project_store = ProjectStore(project_id)
            logger.info(f"Project initialized: {data.get('name')} ({project_id})")

    async def send_chat(self, content: str):
        if not self._connected or not self.ws:
            logger.error("Cannot send chat, disconnected.")
            return

        prof = local_profile.get()
        msg = {
            "type": "chat",
            "content": content,
            "sender_id": prof["user_id"],
            "sender_name": prof["username"]
        }
        await self.ws.send(json.dumps(msg))

    async def disconnect(self):
        self._connected = False
        if self.ws:
            await self.ws.close()
