"""Base scanner class for Tru-Guard."""

from abc import ABC, abstractmethod
from typing import Any

from trusys.types import ActionType, ScanContext, ScanResult, ScanType


class BaseScanner(ABC):
    """Abstract base class for all scanners.

    All custom scanners must inherit from this class and implement
    the `scan` method.

    Attributes:
        name: Scanner name (set by registry decorator or class name)
        scan_types: List of scan types this scanner supports
        on_fail: Action to take when scan fails
        enabled: Whether this scanner is enabled
    """

    # Class-level attributes set by @register_scanner decorator
    _registered_name: str | None = None
    _registered_scan_types: list[ScanType] | None = None
    _validator_type: str | None = None

    def __init__(
        self,
        config: dict[str, Any] | None = None,
        on_fail: ActionType | str | None = None,
        enabled: bool | None = None,
    ) -> None:
        """Initialize the scanner.

        Args:
            config: JSON configuration dictionary with scanner-specific parameters.
                   Also supports 'on_fail' and 'enabled' keys.
            on_fail: Action to take when scan fails (can be in config or as parameter)
            enabled: Whether this scanner is enabled (can be in config or as parameter)
        """
        # Use config dict if provided, otherwise use empty dict
        config = config or {}

        # Extract on_fail from config or parameter (parameter takes precedence)
        on_fail = on_fail if on_fail is not None else config.get("on_fail", ActionType.FAIL)
        if isinstance(on_fail, str):
            on_fail = ActionType(on_fail)

        # Extract enabled from config or parameter (parameter takes precedence)
        enabled = enabled if enabled is not None else config.get("enabled", True)

        self.on_fail = on_fail
        self.enabled = enabled
        self._config = config

    @property
    def name(self) -> str:
        """Get the scanner name."""
        return self._registered_name or self.__class__.__name__

    @property
    def scan_types(self) -> list[ScanType]:
        """Get supported scan types."""
        # Default to supporting both input and output if not registered
        return self._registered_scan_types or [ScanType.INPUT, ScanType.OUTPUT]

    def supports_scan_type(self, scan_type: ScanType) -> bool:
        """Check if this scanner supports the given scan type.

        Args:
            scan_type: The scan type to check

        Returns:
            True if the scanner supports this scan type
        """
        return scan_type in self.scan_types

    @abstractmethod
    def scan(self, content: dict[str, Any], context: ScanContext) -> ScanResult:
        """Perform the scan operation.

        This method must be implemented by all scanner subclasses.

        Args:
            content: JSON dictionary containing content to scan.
                   Typically includes 'prompt' and/or 'response' keys.
                   The scanner should extract the necessary fields for evaluation.
            context: The scan context with additional information

        Returns:
            ScanResult with the scan outcome
        """
        pass

    async def scan_async(self, content: dict[str, Any], context: ScanContext) -> ScanResult:
        """Perform the scan operation asynchronously.

        By default, this calls the synchronous scan method.
        Override this method for scanners that benefit from async I/O.

        Args:
            content: JSON dictionary containing content to scan.
                   Typically includes 'prompt' and/or 'response' keys.
                   The scanner should extract the necessary fields for evaluation.
            context: The scan context with additional information

        Returns:
            ScanResult with the scan outcome
        """
        return self.scan(content, context)

    def validate_content(self, content: dict[str, Any]) -> list[str]:
        """Validate the content before scanning.

        Override this method to add custom validation logic.

        Args:
            content: The content dictionary to validate

        Returns:
            List of validation error messages (empty if valid)
        """
        errors: list[str] = []
        if not content:
            errors.append("Content cannot be empty")
        return errors

    def get_config(self) -> dict[str, Any]:
        """Get the scanner configuration.

        Returns:
            Dictionary of configuration values
        """
        return {
            "name": self.name,
            "on_fail": self.on_fail.value,
            "enabled": self.enabled,
            **self._config,
        }

    def __repr__(self) -> str:
        """String representation of the scanner."""
        return f"{self.__class__.__name__}(name={self.name!r}, on_fail={self.on_fail.value!r})"
