"""server - Server infrastructure for AlbusOS.

Architecture:
    Runtime is the single owner of all components:
    - runtime.turn() → Execute agent turns
    - pathway_service → Manage pathways
    - pathway_vm → Execute pathways

Runtime API:
    from albusos import chat, create_server
    response = await chat("Hello, what can you do?")

    server = await create_server()
    result = await server.turn("host", "Build X", "thread-123")

Modules:
    - server.runtime: Runtime (holds components)
    - server.config: Configuration (DeploymentConfig, AlbusConfig)
    - server.loaders: Load agents, skills, tools, pathways from disk
    - server.transport: HTTP/WebSocket/CLI handlers
"""

from __future__ import annotations

import asyncio
import logging
import uuid
from typing import TYPE_CHECKING

from core.common import JsonObject

if TYPE_CHECKING:
    from server.runtime import Runtime

logger = logging.getLogger(__name__)


# =============================================================================
# RUNTIME API - Chat and pathway execution
# =============================================================================


async def chat(
    message: str,
    *,
    thread_id: str | None = None,
    agent_id: str = "host",
) -> str:
    """Talk to an agent. Returns the response text.

    By default, sends to the "host" agent which can:
    - Create pathways (via pathway.* tools)
    - Spawn other agents (via agent.* tools)
    - Use any available tools

    Args:
        message: What you want the agent to do
        thread_id: For conversation continuity (auto-generated if not provided)
        agent_id: Which agent to talk to (default: "host")

    Returns
    -------
        Response as a string

    Example:
        response = await chat("Create a tool that fetches weather data")
        print(response)
    """
    server = await create_server()
    if thread_id is None:
        thread_id = str(uuid.uuid4())
    result = await server.turn(
        agent_id=agent_id,
        message=message,
        thread_id=thread_id,
    )
    return result.response


async def run(
    pathway_id: str,
    inputs: JsonObject | None = None,
) -> JsonObject:
    """Run a pathway and return results.

    Args:
        pathway_id: ID of the pathway to run
        inputs: Input data for the pathway

    Returns
    -------
        Pathway execution results

    Example:
        result = await run("news-summarizer", {"topic": "AI"})
    """
    server = await create_server()

    # Execute pathway through server's pathway service
    if not server.pathway_service:
        return {"success": False, "error": "Pathway service not available"}

    pathway = server.pathway_service.load(pathway_id)
    if pathway is None:
        return {"success": False, "error": f"Pathway not found: {pathway_id}"}

    if not server.pathway_vm:
        return {"success": False, "error": "Pathway VM not available"}

    record = await server.pathway_vm.execute(pathway, inputs)

    return {
        "success": record.success,
        "outputs": record.outputs,
        "error": record.error,
    }


# =============================================================================
# SERVER FACTORY - For developers who need full control
# =============================================================================

_cached_server: Runtime | None = None
_create_lock: asyncio.Lock | None = None


def _get_create_lock() -> asyncio.Lock:
    """Lazily create the lock (must be created inside a running event loop)."""
    global _create_lock
    if _create_lock is None:
        _create_lock = asyncio.Lock()
    return _create_lock


async def create_server(*, fresh: bool = False) -> Runtime:
    """Create a Runtime instance.

    By default, returns a cached singleton for efficiency.
    Pass fresh=True to create a new instance.
    """
    global _cached_server

    if not fresh and _cached_server is not None:
        return _cached_server

    async with _get_create_lock():
        if not fresh and _cached_server is not None:
            return _cached_server

        from server.config.env import get_config
        from server.runtime import Runtime as _Runtime

        config = get_config()

        store = None
        try:
            from persistence import FileStudioStore

            store = FileStudioStore()
        except ImportError:
            logger.debug("persistence module not available — running without studio store")
        except Exception:
            logger.warning("Failed to initialise FileStudioStore", exc_info=True)

        server = await _Runtime.create(
            studio_store=store,
            persist_dir=config.persistence.persist_dir,
        )

        if not fresh:
            _cached_server = server

        return server


# =============================================================================
# CONVENIENCE SYNC WRAPPERS
# =============================================================================


def chat_sync(message: str, **kwargs) -> str:
    """Synchronous version of chat(). For scripts and notebooks."""
    return asyncio.run(chat(message, **kwargs))


def run_sync(pathway_id: str, inputs: JsonObject | None = None) -> JsonObject:
    """Synchronous version of run(). For scripts and notebooks."""
    return asyncio.run(run(pathway_id, inputs))


# =============================================================================
# PUBLIC API
# =============================================================================

__all__ = [
    # Runtime API
    "chat",
    "chat_sync",
    "create_server",
    "run",
    "run_sync",
]
