"""Plugin implementation"""

from pathlib import Path
from typing import override

from porringer.core.plugin_schema.python_environment import PythonEnvironment
from porringer.core.schema import Package, PackageRef, PluginParameters


class UvEnvironment(PythonEnvironment):
    """Represents a Python environment managed by uv.

    Provides methods to install, search, uninstall, upgrade, and list Python packages using uv
    as the backend package manager.
    """

    def __init__(self, parameters: PluginParameters) -> None:
        """Initializes the uv environment plugin."""
        super().__init__(parameters)
        self._cached_packages: list[Package] | None = None
        self._cached_python: str | None = None

    def _python_args(self) -> list[str]:
        """Return `['--python', '<path>']` when an override is active.

        Uses `self.python_command` (inherited from `PythonEnvironment`)
        to check whether a runtime override is set.
        """
        if self.runtime_executable is not None:
            return ['--python', self.python_command]
        return []

    @classmethod
    @override
    def tool_name(cls) -> str:
        """UV wraps the `uv` CLI."""
        return 'uv'

    @override
    def install_command(self, package: PackageRef) -> list[str]:
        """Returns the CLI command to install a package via uv."""
        return ['uv', 'pip', 'install', *self._python_args(), package.specifier]

    @override
    def upgrade_command(self, package: PackageRef) -> list[str]:
        """Returns the CLI command to upgrade a package via uv."""
        return [
            'uv',
            'pip',
            'install',
            '--upgrade',
            *self._python_args(),
            package.specifier,
        ]

    @override
    def packages(self, *, project_path: Path | None = None) -> list[Package]:
        """Gathers installed packages using `uv pip list --format=json`.

        When *project_path* is provided, the method discovers the
        project's virtual environment (`<project_path>/.venv`) and
        lists packages from that interpreter.  Otherwise it falls back
        to the runtime-override or the default Python.

        Results are cached per effective interpreter so multiple calls
        within a single sync run don't shell out repeatedly.

        Args:
            project_path: Optional project directory.  When set, the
                listing is scoped to the project's `.venv`.

        Returns:
            A list of installed packages.
        """
        # Determine the effective Python target
        effective_args = self._python_args()
        if project_path is not None:
            venv_python = self._discover_venv_python(project_path)
            if venv_python is not None:
                effective_args = ['--python', str(venv_python)]

        cache_key = str(effective_args)
        if self._cached_packages is not None and self._cached_python == cache_key:
            return self._cached_packages

        entries = self._run_json_command(['uv', 'pip', 'list', '--format=json', *effective_args])
        if isinstance(entries, list):
            self._cached_packages = [Package(name=e['name'], version=e.get('version')) for e in entries]
        else:
            self._cached_packages = []

        self._cached_python = cache_key
        return self._cached_packages
