"""Pathway Registry — in-memory lookup and metadata.

Pure in-memory registry for pathway objects. No persistence logic.
"""

import logging
from collections.abc import Callable
from enum import Enum

from pydantic import BaseModel

from core.pathways.pathway import Pathway

logger = logging.getLogger(__name__)


class PathwaySource(Enum):
    """Origin of a pathway."""

    USER = "user"  # Created via API/tools
    IMPORT = "import"  # Imported from export
    PROGRAMMATIC = "programmatic"  # Registered via deploy() in code/tests


class PathwayMeta(BaseModel):
    """Metadata for a registered pathway."""

    id: str
    name: str | None
    description: str | None
    source: str  # e.g., "user:session_xyz" or "test"
    version: str | None
    doc_id: str | None  # Document ID if persisted
    node_count: int


class PathwayRegistry:
    """In-memory pathway registry.

    Stores pathway objects/builders and their metadata for fast lookup.
    """

    def __init__(self) -> None:
        self._registry: dict[str, Callable[[], Pathway] | Pathway] = {}
        self._meta: dict[str, PathwayMeta] = {}

    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 in memory.

        Args:
            pathway_or_builder: Pathway object or lazy builder function
            pathway_id: ID to register under (defaults to pathway.id)
            source: Origin identifier
            version: Version string

        Returns
        -------
            PathwayMeta with registration info
        """
        if callable(pathway_or_builder):
            pathway = pathway_or_builder()
            builder = pathway_or_builder
        else:
            pathway = pathway_or_builder

            def builder(p: Pathway = pathway) -> Pathway:
                return p

        pid = pathway_id or pathway.id

        node_count = len(pathway.nodes)
        if node_count == 0:
            parse_errors = pathway.metadata.get("_parse_errors", [])
            defined_count = pathway.metadata.get("_defined_node_count", 0)
            if defined_count > 0:
                logger.error(
                    "Deploying pathway '%s' with 0 nodes (but %d were defined in YAML). "
                    "This indicates a parsing failure. Parse errors: %s",
                    pid,
                    defined_count,
                    parse_errors
                    or [
                        "Check node types match supported types: llm, tool, transform, input, output, pathway, checkpoint",
                    ],
                )
            else:
                logger.warning(
                    "Deploying pathway '%s' with 0 nodes. "
                    "This pathway will execute instantly and produce no outputs.",
                    pid,
                )

        self._registry[pid] = builder

        meta = PathwayMeta(
            id=pid,
            name=pathway.name,
            description=pathway.description,
            source=source,
            version=version,
            doc_id=None,
            node_count=node_count,
        )
        self._meta[pid] = meta
        logger.debug("Deployed pathway: %s (source=%s, nodes=%d)", pid, source, node_count)
        return meta

    def get(self, pathway_id: str) -> Pathway | None:
        """Get a pathway from the in-memory registry."""
        entry = self._registry.get(pathway_id)
        if entry is None:
            return None
        if callable(entry):
            return entry()
        return entry

    def put(self, pathway_id: str, pathway: Pathway) -> None:
        """Cache a pathway in the registry."""
        self._registry[pathway_id] = pathway

    def resolve(self, pathway_id: str) -> Callable[[], Pathway] | None:
        """Get a lazy loader for a pathway."""
        entry = self._registry.get(pathway_id)
        if entry is None:
            return None
        if callable(entry):
            return entry
        return lambda p=entry: p

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

    def set_meta(self, pathway_id: str, meta: PathwayMeta) -> None:
        """Set metadata for a pathway."""
        self._meta[pathway_id] = meta

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

        for meta in self._meta.values():
            if meta.id in seen_ids:
                continue
            seen_ids.add(meta.id)

            if source_filter and not meta.source.startswith(source_filter):
                continue
            results.append(meta)

        return results

    def delete(self, pathway_id: str) -> PathwayMeta | None:
        """Remove a pathway from the registry. Returns the removed meta."""
        self._registry.pop(pathway_id, None)
        return self._meta.pop(pathway_id, None)

    def contains(self, pathway_id: str) -> bool:
        """Check if a pathway exists in the registry."""
        return pathway_id in self._registry or pathway_id in self._meta


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