"""Language adapter protocol and registry.

Defines the interface that all language-specific adapters implement.
Uses typing.Protocol for structural subtyping — adapters just need
to implement the methods, no inheritance required.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol

if TYPE_CHECKING:
    from collections.abc import Sequence
    from pathlib import Path

    from unfamiliar.analysis.models import ParsedFile


class LanguageAdapter(Protocol):
    """Protocol for language-specific source code parsing.

    Adapters handle parsing source files, extracting imports and definitions,
    and resolving imports to actual file paths within a repository.
    """

    def language_name(self) -> str:
        """Return the name of the language this adapter handles.

        Returns:
            Lowercase language name (e.g., "python", "javascript").
        """
        ...

    def supported_extensions(self) -> frozenset[str]:
        """Return file extensions this adapter handles.

        Returns:
            Set of extensions including the leading dot (e.g., {".py"}).
        """
        ...

    def resolution_extensions(self) -> Sequence[str]:
        """Extensions to try when resolving imports, in preference order.

        Default implementation returns supported_extensions() as a list.
        Subclasses may override to specify a different order.
        """
        return list(self.supported_extensions())

    def can_handle(self, path: str) -> bool:
        """Check if this adapter can parse the given file.

        Args:
            path: File path (typically checked by extension).

        Returns:
            True if this adapter can parse the file.
        """
        ...

    def parse_file(self, path: Path, content: bytes) -> ParsedFile:
        """Parse a file's contents and return structured results.

        This method should never raise exceptions. On parse errors,
        return a ParsedFile with empty lists and log a warning.

        Args:
            path: Path to the file (relative to repo root).
            content: Raw file contents as bytes.

        Returns:
            ParsedFile with imports, definitions, entry points, and complexity.
        """
        ...

    def resolve_import(
        self,
        importing_file: str,
        import_target: str,
        repo_files: set[str],
    ) -> str | None:
        """Resolve an import to an actual file path in the repo.

        Args:
            importing_file: Path of the file containing the import.
            import_target: The raw import string (e.g., "os.path", ".utils").
            repo_files: Set of all file paths in the repository.

        Returns:
            Resolved file path relative to repo root, or None if the import
            targets an external package (stdlib, third-party, etc.).
        """
        ...


class AdapterRegistry:
    """Registry for language adapters.

    Manages registered adapters and dispatches files to the correct one.
    Order of registration determines priority when multiple adapters
    match the same extension.
    """

    def __init__(self) -> None:
        self._adapters: list[LanguageAdapter] = []

    def register(self, adapter: LanguageAdapter) -> None:
        """Register a language adapter.

        Args:
            adapter: The adapter to register.
        """
        self._adapters.append(adapter)

    def get_adapter(self, path: str) -> LanguageAdapter | None:
        """Get the first adapter that can handle the given file.

        Args:
            path: File path to find an adapter for.

        Returns:
            The first matching adapter, or None if no adapter handles this file.
        """
        for adapter in self._adapters:
            if adapter.can_handle(path):
                return adapter
        return None

    def supported_languages(self) -> dict[str, frozenset[str]]:
        """Get all supported languages and their extensions.

        Returns:
            Dict mapping adapter class name to its supported extensions.
        """
        result: dict[str, frozenset[str]] = {}
        for adapter in self._adapters:
            name = adapter.language_name()
            result[name] = adapter.supported_extensions()
        return result
