"""Shared file sync utilities for project workspace."""

import base64
import hashlib
import logging
from typing import Optional

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

MAX_FILE_SIZE = 5 * 1024 * 1024  # 5 MB


def apply_incoming_file(store, msg: dict) -> bool:
    """Apply an incoming project_file_push to a local ProjectStore.

    Returns True if the file was written, False if skipped.
    """
    rel_path = msg.get("relative_path", "")
    content_b64 = msg.get("content_b64", "")
    incoming_hash = msg.get("content_hash", "")
    incoming_ts = msg.get("timestamp", "")
    user_id = msg.get("user_id", "")
    user_name = msg.get("user_name", "")

    if not rel_path or not content_b64:
        return False

    try:
        content = base64.b64decode(content_b64)
    except Exception:
        return False

    # Size check
    if len(content) > MAX_FILE_SIZE:
        logger.warning("Incoming file %s exceeds 5MB limit (%d bytes), skipping",
                        rel_path, len(content))
        return False

    # Verify hash
    actual_hash = hashlib.sha256(content).hexdigest()
    if incoming_hash and actual_hash != incoming_hash:
        logger.warning("Hash mismatch for %s (claimed %s, got %s), skipping",
                        rel_path, incoming_hash[:12], actual_hash[:12])
        return False

    # Conflict check (last-write-wins)
    local_meta = store.get_file_meta(rel_path)
    if local_meta and local_meta["content_hash"] == actual_hash:
        return False  # Already identical

    if local_meta and incoming_ts < local_meta["last_modified_at"]:
        logger.info("Rejecting older version of %s (local is newer)", rel_path)
        return False

    # Apply
    store.add_file(rel_path, content, user_id=user_id, user_name=user_name)
    logger.info("Synced file: %s (%d bytes) from %s", rel_path, len(content), user_name)
    return True


def notify_project_file_changed(file_path: str, room_manager):
    """If a file was written inside a project, sync it to the room."""
    # We need a way to find the active store without circular imports
    from runtime.rooms.room_host import RoomHost
    from runtime.rooms.room_client import RoomClient
    
    store = None
    if room_manager.is_host and room_manager.host_service:
        store = getattr(room_manager.host_service, 'project_store', None)
    elif room_manager.client_service:
        store = getattr(room_manager.client_service, 'project_store', None)
        
    if not store:
        return

    from pathlib import Path
    import base64
    import json
    
    project_files_dir = store.files_dir
    try:
        # Resolve to handle symlinks and relative paths
        target = Path(file_path).resolve()
        root = project_files_dir.resolve()
        rel_path = target.relative_to(root)
    except ValueError:
        return  # Not inside project

    # Read the file and push
    try:
        content = target.read_bytes()
        result = store.add_file(
            str(rel_path).replace("\\", "/"),
            content,
            user_id="ai_agent",
            user_name="AI"
        )

        push_msg = {
            "type": "project_file_push",
            "project_id": store.project_id,
            "relative_path": str(rel_path).replace("\\", "/"),
            "content_b64": base64.b64encode(content).decode("ascii"),
            "content_hash": result["content_hash"],
            "size_bytes": result["size_bytes"],
            "action": result["action"],
            "user_id": "ai_agent",
            "user_name": "AI",
            "timestamp": result["timestamp"]
        }
        room_manager.send_message_raw(push_msg)
    except Exception as e:
        logger.error("Failed to notify project file change: %s", e)


def validate_project_message(msg: dict) -> bool:
    """Basic validation for incoming project WebSocket messages."""
    msg_type = msg.get("type", "")
    if not msg_type.startswith("project_"):
        return True  # Not a project message, pass through

    # Must have project_id
    if not msg.get("project_id"):
        return False

    # file_push must have content and path
    if msg_type == "project_file_push":
        if not msg.get("relative_path") or not msg.get("content_b64"):
            return False
        # Verify base64 is decodable
        try:
            base64.b64decode(msg["content_b64"])
        except Exception:
            return False

    # file_request must have path
    if msg_type == "project_file_request":
        if not msg.get("relative_path"):
            return False

    return True
