"""PathwayService — facade composing registry, loader, sessions, editor.

External callers use pathway_service.load(), pathway_service.list(), etc.
The facade delegates to focused subsystem modules.
"""

import logging
from collections.abc import Callable
from typing import Any

from core.common import JsonObject
from core.pathways.editor import PathwayEditor
from core.pathways.loader import (
    node_from_dict,
    pathway_from_dict,
)
from core.pathways.pathway import Connection, Pathway
from core.pathways.registry import PathwayMeta, PathwayRegistry, PathwaySource
from core.pathways.session import PathwaySession, PathwaySessionManager
from core.pathways.store import PathwayStore, serialize_node  # serialize_node re-exported

logger = logging.getLogger(__name__)


class PathwayService:
    """Unified pathway service — delegates to focused subsystems.

    Provides the same API surface as the old monolithic PathwayService
    while internally composing: PathwayRegistry, PathwayStore,
    PathwaySessionManager, PathwayEditor, and loader functions.
    """

    def __init__(self) -> None:
        self._registry = PathwayRegistry()
        self._store = PathwayStore()
        self._sessions = PathwaySessionManager()
        self._editor = PathwayEditor(
            registry=self._registry,
        )

    # =========================================================================
    # REGISTRATION
    # =========================================================================

    def deploy(
        self,
        pathway_or_builder: Pathway | Callable[[], Pathway],
        *,
        pathway_id: str | None = None,
        source: str = "programmatic",
        version: str | None = None,
    ) -> PathwayMeta:
        """Register a pathway programmatically."""
        return self._registry.deploy(
            pathway_or_builder,
            pathway_id=pathway_id,
            source=source,
            version=version,
        )

    # =========================================================================
    # SAVE / CREATE
    # =========================================================================

    def save(self, pathway: Pathway) -> None:
        """Save (upsert) a pathway in memory."""
        self._registry.put(pathway.id, pathway)

    # -------------------------------------------------------------------------

    def create(
        self,
        pathway: Pathway,
        *,
        source: str = "user:unknown",
    ) -> PathwayMeta:
        """Create a user pathway."""
        return self._store.create_user_pathway(
            pathway,
            source=source,
            registry=self._registry,
        )

    # =========================================================================
    # LOADING
    # =========================================================================

    def load(self, pathway_id: str) -> Pathway | None:
        """Load a pathway by ID.

        Checks the session-local registry first, then falls back to the
        workspace-level registry populated by :func:`load_skill` and
        :func:`load_workspace`.
        """
        result = self._registry.get(pathway_id)
        if result is not None:
            return result
        # Fallback: workspace pathway registry (populated by load_skill / load_workspace).
        from core.workspace import get_workspace_pathway_registry

        ws_registry = get_workspace_pathway_registry()
        if ws_registry is not None:
            return ws_registry.get(pathway_id)
        return None

    def resolve(self, pathway_id: str) -> Callable[[], Pathway] | None:
        """Get a lazy loader for a pathway."""
        return self._registry.resolve(pathway_id)

    def get_meta(self, pathway_id: str) -> PathwayMeta | None:
        """Get metadata for a pathway."""
        return self._registry.get_meta(pathway_id)

    # =========================================================================
    # LISTING
    # =========================================================================

    def list(self, *, source_filter: str | None = None) -> list[PathwayMeta]:
        """List all registered pathways."""
        return self._registry.list(source_filter=source_filter)

    # =========================================================================
    # EXPORT / IMPORT
    # =========================================================================

    def export(self, pathway_id: str) -> JsonObject | None:
        """Export a pathway as a portable JSON document."""
        pathway = self.load(pathway_id)
        if pathway is None:
            return None
        meta = self._registry.get_meta(pathway_id)
        return self._store.export(pathway, meta)

    def import_pathway(
        self,
        data: JsonObject | dict[str, Any],
        *,
        new_id: str | None = None,
    ) -> PathwayMeta:
        """Import a pathway from an export."""
        if data.get("format") != "albusos.pathway.v1":
            msg = f"Unsupported format: {data.get('format')}"
            raise ValueError(msg)

        pathway_data = data.get("pathway", {})
        pathway = pathway_from_dict(pathway_data)

        if new_id:
            pathway.id = new_id

        return self.create(
            pathway,
            source=f"import:{data.get('meta', {}).get('source', 'unknown')}",
        )

    # =========================================================================
    # NODE CRUD (Editor delegation)
    # =========================================================================

    def add_node(self, pathway_id: str, node_data: dict[str, Any]) -> tuple[Pathway, Any]:
        return self._editor.add_node(pathway_id, node_data)

    def update_node(
        self,
        pathway_id: str,
        node_id: str,
        updates: JsonObject | dict[str, Any],
    ) -> tuple[Pathway, Any]:
        return self._editor.update_node(pathway_id, node_id, updates)

    def delete_node(
        self,
        pathway_id: str,
        node_id: str,
        *,
        remove_connections: bool = True,
    ) -> Pathway:
        return self._editor.delete_node(pathway_id, node_id, remove_connections=remove_connections)

    def add_connection(
        self,
        pathway_id: str,
        conn_data: JsonObject | dict[str, Any],
    ) -> tuple[Pathway, Connection]:
        return self._editor.add_connection(pathway_id, conn_data)

    def delete_connection(
        self,
        pathway_id: str,
        from_node: str,
        to_node: str,
    ) -> Pathway:
        return self._editor.delete_connection(pathway_id, from_node, to_node)

    # =========================================================================
    # PATHWAY METADATA & DELETE
    # =========================================================================

    def update(
        self,
        pathway_id: str,
        *,
        name: str | None = None,
        description: str | None = None,
        metadata: JsonObject | None = None,
    ) -> PathwayMeta:
        """Update pathway metadata."""
        return self._editor.update_metadata(
            pathway_id,
            name=name,
            description=description,
            metadata=metadata,
        )

    def delete(self, pathway_id: str) -> bool:
        """Delete a pathway from the in-memory registry."""
        if not self._registry.contains(pathway_id):
            return False

        self._registry.delete(pathway_id)
        logger.debug("Deleted pathway: %s", pathway_id)
        return True

    # =========================================================================
    # SESSION PERSISTENCE (delegation)
    # =========================================================================

    async def save_session(self, session: PathwaySession) -> str:
        return await self._sessions.save(session)

    async def load_session(self, session_id: str) -> PathwaySession | None:
        return await self._sessions.load(session_id)

    async def delete_session(self, session_id: str) -> bool:
        return await self._sessions.delete(session_id)

    def list_sessions(
        self,
        *,
        pathway_id: str | None = None,
        status: str | None = None,
    ) -> "list[PathwaySession]":
        return self._sessions.list_sessions(pathway_id=pathway_id, status=status)

    # =========================================================================
    # INTERNAL (for backward compat — delegate to modules)
    # =========================================================================

    def _serialize_node(self, node: Any) -> JsonObject:
        return serialize_node(node)

    def _node_from_dict(self, node_dict: JsonObject | dict[str, Any]) -> Any:
        return node_from_dict(node_dict)

    def _pathway_from_dict(self, data: JsonObject | dict[str, Any]) -> Pathway:
        return pathway_from_dict(data)


__all__ = [
    "PathwayMeta",
    "PathwayService",
    "PathwaySource",
]
