"""Reader protocol and registry for crash test data formats.

All readers implement the ReaderProtocol, enabling pluggable format support.
The ReaderRegistry auto-detects formats and dispatches to the correct reader.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Protocol, runtime_checkable

from impakt.channel.model import TestData, TestMetadata

logger = logging.getLogger(__name__)


@runtime_checkable
class ReaderProtocol(Protocol):
    """Protocol for crash test data readers.

    Each reader handles one file format (MME, TDMS, CSV, etc.).
    """

    @property
    def format_name(self) -> str:
        """Human-readable format name (e.g., 'ISO 13499 MME')."""
        ...

    def supports(self, path: Path) -> bool:
        """Check if this reader can handle the given path.

        Args:
            path: Path to a file or directory.

        Returns:
            True if this reader can read the given path.
        """
        ...

    def metadata(self, path: Path) -> TestMetadata:
        """Read only the test metadata without loading channel data.

        Useful for browsing/cataloging tests without the memory cost.
        """
        ...

    def read(self, path: Path) -> TestData:
        """Read the full test data including all channels.

        Args:
            path: Path to the test data (file or directory).

        Returns:
            TestData with all channels populated.
        """
        ...


class ReaderRegistry:
    """Registry of available data readers.

    Supports auto-detection: given a path, tries each registered reader
    in priority order until one claims support.
    """

    def __init__(self) -> None:
        self._readers: list[ReaderProtocol] = []

    def register(self, reader: ReaderProtocol) -> None:
        """Register a reader. Later registrations have higher priority."""
        self._readers.append(reader)
        logger.info("Registered reader: %s", reader.format_name)

    def detect(self, path: Path) -> ReaderProtocol | None:
        """Detect the appropriate reader for a path.

        Tries readers in reverse registration order (latest first).

        Returns:
            The first reader that supports the path, or None.
        """
        resolved = Path(path).resolve()
        for reader in reversed(self._readers):
            if reader.supports(resolved):
                logger.debug("Detected format: %s for %s", reader.format_name, path)
                return reader
        return None

    def read(self, path: Path) -> TestData:
        """Read test data, auto-detecting the format.

        Raises:
            ValueError: If no registered reader supports the path.
        """
        reader = self.detect(path)
        if reader is None:
            raise ValueError(
                f"No reader found for path: {path}\n"
                f"Registered readers: {[r.format_name for r in self._readers]}"
            )
        return reader.read(Path(path).resolve())

    def metadata(self, path: Path) -> TestMetadata:
        """Read test metadata, auto-detecting the format."""
        reader = self.detect(path)
        if reader is None:
            raise ValueError(f"No reader found for path: {path}")
        return reader.metadata(Path(path).resolve())

    @property
    def readers(self) -> list[ReaderProtocol]:
        """List of registered readers."""
        return list(self._readers)


# Global registry instance
_global_registry = ReaderRegistry()


def get_registry() -> ReaderRegistry:
    """Get the global reader registry."""
    return _global_registry


def register_reader(reader: ReaderProtocol) -> None:
    """Register a reader in the global registry."""
    _global_registry.register(reader)


def read(path: str | Path) -> TestData:
    """Read test data from a path using the global registry."""
    return _global_registry.read(Path(path))
