"""Service layer for managing skill registrations.

Orchestrates persistence through ``SkillRepository``, providing a clean
API for CLI and higher-level orchestrators.

Includes Agent Skills discovery via configurable paths
(``skills.agent_skills_paths``) with refresh-on-demand semantics.
"""

from __future__ import annotations

import logging
from typing import Any

from cleveragents.domain.models.core.skill import Skill
from cleveragents.infrastructure.database.repositories import (
    SkillRepository,
)
from cleveragents.skills.discovery import (
    DiscoveryConflict,
    DiscoveryResult,
    discover_agent_skills,
    parse_agent_skills_paths,
    register_discovered_skills,
)
from cleveragents.tool.runtime import ToolSpec

logger = logging.getLogger(__name__)


class SkillRegistryService:
    """Coordinate skill registration and lifecycle management.

    Delegates all persistence to the repository layer.  Designed to be
    injected with a ``SkillRepository`` (using session-factory pattern).

    Provides Agent Skills discovery via ``discover_and_register`` and a
    refresh hook (``refresh_agent_skills``) that re-scans configured
    paths and updates the ``ToolRegistry``.
    """

    def __init__(
        self,
        skill_repo: SkillRepository,
        tool_registry: Any | None = None,
    ) -> None:
        self._skill_repo = skill_repo
        self._tool_registry = tool_registry
        self._last_discovery: DiscoveryResult | None = None
        self._registered_agent_tools: list[ToolSpec] = []
        self._discovery_conflicts: list[DiscoveryConflict] = []

    # -- Skill CRUD --------------------------------------------------------

    def add_skill(self, skill: Skill) -> Skill:
        """Register a new skill.

        Args:
            skill: A ``Skill`` domain model instance.

        Returns:
            The registered skill.

        Raises:
            DuplicateSkillError: If the name already exists.
            DatabaseError: On persistence failure.
        """
        return self._skill_repo.create(skill)

    def get_skill(self, name: str) -> Skill | None:
        """Retrieve a single skill by name.

        Args:
            name: The namespaced name.

        Returns:
            The ``Skill`` domain object, or ``None`` if not found.
        """
        return self._skill_repo.get_by_name(name)

    def list_skills(
        self,
        namespace: str | None = None,
    ) -> list[Skill]:
        """List skills with optional filters.

        Args:
            namespace: Optional namespace filter.

        Returns:
            List of ``Skill`` domain objects.
        """
        return self._skill_repo.list_all(namespace=namespace)

    def update_skill(self, skill: Skill) -> Skill:
        """Update an existing skill definition.

        Args:
            skill: A ``Skill`` domain object with updated fields.

        Returns:
            The updated skill.

        Raises:
            SkillNotFoundError: If the skill is not found.
            DatabaseError: On persistence failure.
        """
        return self._skill_repo.update(skill)

    def remove_skill(self, name: str) -> bool:
        """Remove a skill from the registry.

        Args:
            name: The namespaced name of the skill.

        Returns:
            ``True`` if the skill was removed, ``False`` if not found.
        """
        return self._skill_repo.delete(name)

    # -- Agent Skills discovery --------------------------------------------

    def discover_and_register(
        self,
        raw_paths: str,
        *,
        on_conflict: str = "skip",
    ) -> DiscoveryResult:
        """Run agent skills discovery and register tools.

        Parses the comma-separated *raw_paths* string, scans each
        directory for Agent Skills Standard folders, and registers the
        discovered tools in the ``ToolRegistry``.

        Args:
            raw_paths: Comma-separated directory paths to scan.
            on_conflict: Conflict strategy — ``"skip"`` (default),
                ``"error"``, or ``"replace"``.

        Returns:
            ``DiscoveryResult`` with discovered skills and any errors.
        """
        if not raw_paths:
            return DiscoveryResult()

        paths = parse_agent_skills_paths(raw_paths)
        result = discover_agent_skills(paths)
        self._last_discovery = result

        if result.discovered and self._tool_registry is not None:
            registered, conflicts = register_discovered_skills(
                result.discovered,
                self._tool_registry,
                on_conflict=on_conflict,
            )
            self._registered_agent_tools = registered
            self._discovery_conflicts = conflicts
            result.conflicts = conflicts

        return result

    def refresh_agent_skills(
        self,
        raw_paths: str,
        *,
        on_conflict: str = "skip",
    ) -> DiscoveryResult:
        """Re-scan Agent Skills paths and update registrations.

        Removes previously discovered agent skill tools from the
        ``ToolRegistry`` before re-scanning and re-registering.

        Args:
            raw_paths: Comma-separated directory paths to scan.
            on_conflict: Conflict strategy.

        Returns:
            Fresh ``DiscoveryResult``.
        """
        # Remove previously registered agent skill tools
        if self._tool_registry is not None:
            for spec in self._registered_agent_tools:
                self._tool_registry.remove(spec.name)
        self._registered_agent_tools = []
        self._discovery_conflicts = []

        return self.discover_and_register(raw_paths, on_conflict=on_conflict)

    @property
    def last_discovery(self) -> DiscoveryResult | None:
        """Return the result of the most recent discovery scan."""
        return self._last_discovery

    @property
    def registered_agent_tools(self) -> list[ToolSpec]:
        """Return the list of currently registered agent skill tools."""
        return list(self._registered_agent_tools)

    @property
    def discovery_conflicts(self) -> list[DiscoveryConflict]:
        """Return conflicts from the most recent discovery scan."""
        return list(self._discovery_conflicts)
