"""
Agent Listing Service for CLI commands
=======================================

WHY: The agent listing logic was previously embedded in the agents CLI command,
making it difficult to test and reuse. This service extracts that functionality
into a reusable, testable component that can be used by multiple CLI commands.

DESIGN DECISIONS:
- Interface-based design for dependency injection and testing
- Caching for performance optimization
- Integration with AgentRegistryAdapter for unified agent discovery
- Support for multiple listing modes (system, deployed, by-tier)
- Comprehensive error handling and logging
"""

import time
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from ...core.agent_registry import AgentRegistryAdapter
from ...core.logger import get_logger
from ...services.agents.deployment import AgentDeploymentService
from ...services.agents.deployment.deployment_wrapper import DeploymentServiceWrapper


@dataclass
class AgentInfo:
    """Information about an agent."""

    name: str
    type: str
    tier: str
    path: str
    description: str | None = None
    specializations: list[str] | None = None
    version: str | None = None
    deployed: bool = False
    active: bool = True
    overridden_by: list[str] | None = None


@dataclass
class AgentTierInfo:
    """Information about agents grouped by tier."""

    project: list[AgentInfo]
    user: list[AgentInfo]
    system: list[AgentInfo]

    @property
    def total_count(self) -> int:
        """Get total number of agents across all tiers."""
        return len(self.project) + len(self.user) + len(self.system)

    @property
    def active_count(self) -> int:
        """Get count of active agents (not overridden)."""
        count = 0
        for agents in [self.project, self.user, self.system]:
            count += sum(1 for a in agents if a.active)
        return count


class IAgentListingService(ABC):
    """Interface for agent listing service."""

    @abstractmethod
    def list_system_agents(self, verbose: bool = False) -> list[AgentInfo]:
        """
        List available system agent templates.

        Args:
            verbose: Include detailed metadata

        Returns:
            List of system agent information
        """

    @abstractmethod
    def list_deployed_agents(
        self, verbose: bool = False
    ) -> tuple[list[AgentInfo], list[str]]:
        """
        List currently deployed agents.

        Args:
            verbose: Include detailed metadata

        Returns:
            Tuple of (agent list, warnings)
        """

    @abstractmethod
    def list_agents_by_tier(self) -> AgentTierInfo:
        """
        List agents grouped by tier/precedence.

        Returns:
            Agent information grouped by tier
        """

    @abstractmethod
    def get_agent_details(self, agent_name: str) -> dict[str, Any] | None:
        """
        Get detailed information for a specific agent.

        Args:
            agent_name: Name of the agent

        Returns:
            Agent details or None if not found
        """

    @abstractmethod
    def compare_versions(self, agent_name: str) -> dict[str, Any]:
        """
        Compare versions of an agent across tiers.

        Args:
            agent_name: Name of the agent

        Returns:
            Version comparison data
        """

    @abstractmethod
    def find_agent(self, agent_name: str) -> AgentInfo | None:
        """
        Find an agent by name across all sources.

        Args:
            agent_name: Name of the agent

        Returns:
            Agent info or None if not found
        """

    @abstractmethod
    def clear_cache(self) -> None:
        """Clear the service cache."""


class AgentListingService(IAgentListingService):
    """Implementation of agent listing service."""

    def __init__(self, deployment_service: AgentDeploymentService | None = None):
        """
        Initialize agent listing service.

        Args:
            deployment_service: Optional deployment service instance
        """
        self.logger = get_logger(self.__class__.__name__)
        self._deployment_service = deployment_service
        self._registry_adapter = None
        self._cache = {}
        self._cache_ttl = 60  # Cache for 60 seconds
        self._cache_times = {}

    @property
    def deployment_service(self) -> AgentDeploymentService:
        """Get deployment service instance (lazy loaded)."""
        if self._deployment_service is None:
            try:
                from ...services import AgentDeploymentService

                base_service = AgentDeploymentService()
                self._deployment_service = DeploymentServiceWrapper(base_service)
            except ImportError as e:
                raise ImportError("Agent deployment service not available") from e
        return self._deployment_service

    @property
    def registry_adapter(self) -> AgentRegistryAdapter:
        """Get registry adapter instance (lazy loaded)."""
        if self._registry_adapter is None:
            self._registry_adapter = AgentRegistryAdapter()
        return self._registry_adapter

    def _is_cache_valid(self, key: str) -> bool:
        """Check if cache entry is still valid."""
        if key not in self._cache_times:
            return False
        return (time.time() - self._cache_times[key]) < self._cache_ttl

    def _get_from_cache(self, key: str) -> Any | None:
        """Get value from cache if valid."""
        if self._is_cache_valid(key):
            return self._cache.get(key)
        return None

    def _set_cache(self, key: str, value: Any) -> None:
        """Set cache value."""
        self._cache[key] = value
        self._cache_times[key] = time.time()

    def list_system_agents(self, verbose: bool = False) -> list[AgentInfo]:
        """List available system agent templates."""
        cache_key = f"system_agents_{verbose}"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            agents_data = self.deployment_service.list_available_agents()
            agents = []

            for agent_data in agents_data:
                agent = AgentInfo(
                    name=agent_data.get("name", ""),
                    type=agent_data.get("agent_type", agent_data.get("type", "agent")),
                    tier="system",
                    path=agent_data.get("path", ""),
                    description=agent_data.get("description") if verbose else None,
                    specializations=(
                        agent_data.get("specializations") if verbose else None
                    ),
                    version=agent_data.get("version") if verbose else None,
                )
                agents.append(agent)

            self._set_cache(cache_key, agents)
            return agents

        except Exception as e:
            self.logger.error(f"Error listing system agents: {e}", exc_info=True)
            return []

    def list_deployed_agents(
        self, verbose: bool = False
    ) -> tuple[list[AgentInfo], list[str]]:
        """List currently deployed agents."""
        cache_key = f"deployed_agents_{verbose}"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            verification = self.deployment_service.verify_deployment()
            agents_data = verification.get("agents_found", [])
            warnings = verification.get("warnings", [])

            agents = []
            for agent_data in agents_data:
                agent = AgentInfo(
                    name=agent_data.get("name", ""),
                    type=agent_data.get("agent_type", agent_data.get("type", "agent")),
                    tier=agent_data.get("tier", "system"),
                    path=agent_data.get("path", ""),
                    description=agent_data.get("description"),
                    specializations=(
                        agent_data.get("specializations") if verbose else None
                    ),
                    version=agent_data.get("version"),
                    deployed=True,
                )
                agents.append(agent)

            result = (agents, warnings)
            self._set_cache(cache_key, result)
            return result

        except Exception as e:
            self.logger.error(f"Error listing deployed agents: {e}", exc_info=True)
            return ([], [f"Error listing deployed agents: {e}"])

    def list_agents_by_tier(self) -> AgentTierInfo:
        """List agents grouped by tier/precedence."""
        cache_key = "agents_by_tier"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            if not self.registry_adapter.registry:
                self.logger.error("Could not initialize agent registry")
                return AgentTierInfo(project=[], user=[], system=[])

            # Get all agents and group by tier
            all_agents = self.registry_adapter.registry.list_agents()

            # Group agents by tier
            tiers = {"project": [], "user": [], "system": []}
            agent_names = set()

            for agent_id, metadata in all_agents.items():
                tier = metadata.get("tier", "system")
                if tier not in tiers:
                    continue

                agent_info = AgentInfo(
                    name=agent_id,
                    type=metadata.get("agent_type", metadata.get("type", "agent")),
                    tier=tier,
                    path=metadata.get("path", ""),
                    description=metadata.get("description"),
                    specializations=metadata.get("specializations", []),
                    deployed=metadata.get("deployed", False),
                )

                tiers[tier].append(agent_info)
                agent_names.add(agent_id)

            # Check for overrides
            for tier_name in ["project", "user", "system"]:
                for agent in tiers[tier_name]:
                    overridden_by = []

                    # Check if overridden by higher tiers
                    if tier_name == "system":
                        # Check if overridden by user or project
                        if any(a.name == agent.name for a in tiers["user"]):
                            overridden_by.append("USER")
                        if any(a.name == agent.name for a in tiers["project"]):
                            overridden_by.append("PROJECT")
                    elif tier_name == "user":
                        # Check if overridden by project
                        if any(a.name == agent.name for a in tiers["project"]):
                            overridden_by.append("PROJECT")

                    if overridden_by:
                        agent.active = False
                        agent.overridden_by = overridden_by

            result = AgentTierInfo(
                project=tiers["project"], user=tiers["user"], system=tiers["system"]
            )

            self._set_cache(cache_key, result)
            return result

        except Exception as e:
            self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
            return AgentTierInfo(project=[], user=[], system=[])

    def get_agent_details(self, agent_name: str) -> dict[str, Any] | None:
        """Get detailed information for a specific agent."""
        cache_key = f"agent_details_{agent_name}"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            # Try to get from deployment service first
            details = self.deployment_service.get_agent_details(agent_name)

            if details:
                self._set_cache(cache_key, details)
                return details

            # Fall back to registry
            if not self.registry_adapter.registry:
                return None

            agent = self.registry_adapter.registry.get_agent(agent_name)
            if not agent:
                return None

            # Read agent file for full content
            agent_path = Path(agent.path)
            if not agent_path.exists():
                return None

            with agent_path.open() as f:
                content = f.read()

            details = {
                "name": getattr(agent, "name", agent_name),
                "type": getattr(agent, "agent_type", None)
                or getattr(agent, "type", "agent"),
                "tier": getattr(agent, "tier", "system"),
                "path": str(getattr(agent, "path", agent_path)),
                "description": getattr(agent, "description", None),
                "specializations": getattr(agent, "specializations", []),
                "content": content,
                "size": agent_path.stat().st_size,
                "modified": agent_path.stat().st_mtime,
            }

            self._set_cache(cache_key, details)
            return details

        except Exception as e:
            self.logger.error(f"Error getting agent details: {e}", exc_info=True)
            return None

    def compare_versions(self, agent_name: str) -> dict[str, Any]:
        """Compare versions of an agent across tiers."""
        cache_key = f"version_compare_{agent_name}"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            tier_info = self.list_agents_by_tier()
            versions = {}

            # Check each tier for the agent
            for tier_name, agents in [
                ("project", tier_info.project),
                ("user", tier_info.user),
                ("system", tier_info.system),
            ]:
                for agent in agents:
                    if agent.name == agent_name:
                        versions[tier_name] = {
                            "path": agent.path,
                            "version": agent.version,
                            "active": agent.active,
                            "overridden_by": agent.overridden_by,
                        }

            result = {
                "agent_name": agent_name,
                "versions": versions,
                "active_tier": next(
                    (tier for tier, v in versions.items() if v.get("active")), None
                ),
            }

            self._set_cache(cache_key, result)
            return result

        except Exception as e:
            self.logger.error(f"Error comparing versions: {e}", exc_info=True)
            return {"agent_name": agent_name, "versions": {}, "error": str(e)}

    def find_agent(self, agent_name: str) -> AgentInfo | None:
        """Find an agent by name across all sources."""
        cache_key = f"find_agent_{agent_name}"
        cached = self._get_from_cache(cache_key)
        if cached is not None:
            return cached

        try:
            # First check deployed agents
            deployed, _ = self.list_deployed_agents()
            for agent in deployed:
                if agent.name == agent_name:
                    self._set_cache(cache_key, agent)
                    return agent

            # Check all tiers
            tier_info = self.list_agents_by_tier()
            for agents in [tier_info.project, tier_info.user, tier_info.system]:
                for agent in agents:
                    if agent.name == agent_name:
                        self._set_cache(cache_key, agent)
                        return agent

            return None

        except Exception as e:
            self.logger.error(f"Error finding agent: {e}", exc_info=True)
            return None

    def clear_cache(self) -> None:
        """Clear the service cache."""
        self._cache.clear()
        self._cache_times.clear()
        self.logger.debug("Agent listing service cache cleared")
