"""Abstract base scanner — all scanner plugins inherit from this class."""

from __future__ import annotations

import logging
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any

from secagent.utils.installer import get_binary_path, ensure_installed

logger = logging.getLogger(__name__)


class BaseScanner(ABC):
    """Interface that every scanner integration must implement.

    Subclasses set ``name``, and optionally ``github_owner`` + ``github_repo``
    to enable automatic installation from GitHub releases.
    """

    name: str = ""
    github_owner: str = ""
    github_repo: str = ""

    @abstractmethod
    def run(self, target: Path) -> list[dict[str, Any]]:
        """Execute the scanner against *target* and return raw JSON findings."""

    def get_executable(self) -> str | None:
        """Return the path to the executable, installing if needed."""
        path = get_binary_path(self.name)
        if path:
            return str(path)

        if self.github_owner and self.github_repo:
            installed = ensure_installed(self.name, self.github_owner, self.github_repo)
            if installed:
                return str(installed)

        return None

    def is_available(self) -> bool:
        """Return *True* if the scanner is installed or can be downloaded."""
        return self.get_executable() is not None

    def safe_run(self, target: Path) -> list[dict[str, Any]]:
        """Run the scanner if it is available, otherwise warn and skip."""
        if not self.is_available():
            logger.warning(
                "Scanner '%s' is not available — skipping.",
                self.name,
            )
            return []
        return self.run(target)
