"""Multi-source agent file discovery for AURORA CLI.

This module provides the AgentScanner class for discovering agent markdown files
from multiple standard directories used by various AI coding assistants:
- ~/.claude/agents/ (Claude Code)
- ~/.config/ampcode/agents/ (AMP Code)
- ~/.config/droid/agent/ (Droid)
- ~/.config/opencode/agent/ (OpenCode)

The scanner handles missing directories gracefully with logging, and yields
file paths for processing by the AgentParser.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Iterator


logger = logging.getLogger(__name__)

# Default agent discovery paths (relative to home directory)
DEFAULT_DISCOVERY_PATHS: list[str] = [
    "~/.claude/agents",
    "~/.config/ampcode/agents",
    "~/.config/droid/agent",
    "~/.config/opencode/agent",
]

# Supported agent file extensions
AGENT_FILE_EXTENSIONS: frozenset[str] = frozenset({".md", ".markdown"})


class AgentScanner:
    """Multi-source agent file scanner.

    Discovers agent markdown files from multiple configured directories,
    handling missing paths gracefully with logging.

    Attributes:
        discovery_paths: List of directories to scan for agent files

    Example:
        >>> scanner = AgentScanner()
        >>> for file_path in scanner.scan_all_sources():
        ...     print(f"Found agent: {file_path}")
        Found agent: /home/user/.claude/agents/qa-test-architect.md
        Found agent: /home/user/.claude/agents/full-stack-dev.md

        >>> # With custom paths
        >>> scanner = AgentScanner(["/custom/agents"])
        >>> sources = scanner.discover_sources()
        >>> print(sources)
        [Path('/custom/agents')]
    """

    def __init__(self, discovery_paths: list[str] | None = None) -> None:
        """Initialize the AgentScanner.

        Args:
            discovery_paths: Optional list of paths to scan. If not provided,
                           uses DEFAULT_DISCOVERY_PATHS. Paths may contain
                           tilde (~) for home directory expansion.
        """
        if discovery_paths is None:
            discovery_paths = DEFAULT_DISCOVERY_PATHS.copy()

        self._raw_paths = discovery_paths

    @property
    def discovery_paths(self) -> list[str]:
        """Get the raw discovery paths (before expansion).

        Returns:
            List of configured discovery paths
        """
        return self._raw_paths.copy()

    def discover_sources(self) -> list[Path]:
        """Return list of existing discovery paths from configuration.

        Expands tilde in paths and filters to only existing directories.
        Logs a debug message for each missing directory.

        Returns:
            List of Path objects for existing agent directories

        Example:
            >>> scanner = AgentScanner()
            >>> existing = scanner.discover_sources()
            >>> for path in existing:
            ...     print(f"Source: {path}")
            Source: /home/user/.claude/agents
        """
        existing_paths: list[Path] = []

        for raw_path in self._raw_paths:
            expanded_path = Path(raw_path).expanduser().resolve()

            if expanded_path.exists() and expanded_path.is_dir():
                existing_paths.append(expanded_path)
                logger.debug("Discovered agent source: %s", expanded_path)
            else:
                logger.debug("Agent source not found (skipping): %s", raw_path)

        return existing_paths

    def scan_directory(self, directory: Path) -> Iterator[Path]:
        """Scan a single directory for agent markdown files.

        Yields paths to all .md and .markdown files in the directory
        (non-recursive). Files are sorted alphabetically for consistent
        ordering.

        Args:
            directory: Directory path to scan

        Yields:
            Path objects for each agent file found

        Example:
            >>> scanner = AgentScanner()
            >>> agent_dir = Path("~/.claude/agents").expanduser()
            >>> for path in scanner.scan_directory(agent_dir):
            ...     print(path.name)
            full-stack-dev.md
            orchestrator.md
            qa-test-architect.md
        """
        if not directory.exists():
            logger.warning("Directory does not exist: %s", directory)
            return

        if not directory.is_dir():
            logger.warning("Path is not a directory: %s", directory)
            return

        try:
            # Get all files with agent extensions, sorted for consistent ordering
            agent_files = sorted(
                f for f in directory.iterdir()
                if f.is_file() and f.suffix.lower() in AGENT_FILE_EXTENSIONS
            )

            for file_path in agent_files:
                logger.debug("Found agent file: %s", file_path)
                yield file_path

        except PermissionError as e:
            logger.warning("Permission denied scanning directory %s: %s", directory, e)
        except OSError as e:
            logger.warning("Error scanning directory %s: %s", directory, e)

    def scan_all_sources(self) -> Iterator[Path]:
        """Scan all configured sources for agent files.

        Discovers existing source directories and yields paths to all
        agent markdown files found. Handles missing directories and
        permission errors gracefully with logging.

        Yields:
            Path objects for each agent file discovered across all sources

        Example:
            >>> scanner = AgentScanner()
            >>> all_agents = list(scanner.scan_all_sources())
            >>> print(f"Found {len(all_agents)} agent files")
            Found 12 agent files
        """
        sources = self.discover_sources()

        if not sources:
            logger.info(
                "No agent source directories found. Checked: %s",
                ", ".join(self._raw_paths),
            )
            return

        logger.info("Scanning %d agent source(s) for agent files", len(sources))

        for source_dir in sources:
            logger.debug("Scanning source: %s", source_dir)
            yield from self.scan_directory(source_dir)

    def get_source_stats(self) -> dict[str, int]:
        """Get statistics about agent files per source directory.

        Returns:
            Dictionary mapping source path to count of agent files found

        Example:
            >>> scanner = AgentScanner()
            >>> stats = scanner.get_source_stats()
            >>> for source, count in stats.items():
            ...     print(f"{source}: {count} agents")
            /home/user/.claude/agents: 8 agents
            /home/user/.config/ampcode/agents: 3 agents
        """
        stats: dict[str, int] = {}

        for source_dir in self.discover_sources():
            count = sum(1 for _ in self.scan_directory(source_dir))
            stats[str(source_dir)] = count

        return stats
