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

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
        
        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")
                    
                    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":
                        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":
                        # Someone pushed a file change — apply locally and broadcast to others
                        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)
                        
            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()
