# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

from __future__ import annotations

import re

from extra_platforms import ALL_PLATFORMS

from ..base import PackageManager
from ..capabilities import search_capabilities, version_not_implemented

TYPE_CHECKING = False
if TYPE_CHECKING:
    from collections.abc import Iterator

    from ..base import Package


class Cargo(PackageManager):
    name = "Rust's cargo"

    homepage_url = "https://doc.rust-lang.org/cargo/"

    platforms = ALL_PLATFORMS

    requirement = "1.0.0"

    pre_args = (
        "--color",
        "never",  # Suppress colored output.
        "--quiet",  # Do not print cargo log messages.
    )

    _INSTALLED_REGEXP = re.compile(
        r"^(?P<package_id>\S+)\s+v(?P<package_version>\S+):$",
    )
    _SEARCH_REGEXP = re.compile(
        r"^(?P<package_id>\S+)\s+=\s+\"(?P<version>\S+)\"\s+#\s+(?P<description>.+)$",
        re.MULTILINE,
    )

    version_regexes = (r"cargo\s+(?P<version>\S+)",)
    """
    .. code-block:: shell-session

        $ cargo --version
        cargo 1.59.0
    """

    @property
    def installed(self) -> Iterator[Package]:
        """Fetch installed packages.

        .. code-block:: shell-session

            $ cargo --color never --quiet install --list
            bore-cli v0.4.0:
                bore
            ripgrep v13.0.0:
                rg
        """
        output = self.run_cli("install", "--list")

        for package in output.splitlines():
            match = self._INSTALLED_REGEXP.match(package)
            if match:
                package_id, package_version = match.groups()
                yield self.package(id=package_id, installed_version=package_version)

    @search_capabilities(extended_support=False, exact_support=False)
    def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]:
        """Fetch matching packages.

        .. caution::
            Search does not support extended or exact matching. So we returns the best
            subset of results and let
            :py:meth:`meta_package_manager.base.PackageManager.refiltered_search` refine
            them.

        .. danger:
            `Cargo limits search to 100 results
            <https://doc.rust-lang.org/cargo/commands/cargo-search.html#search-options>`_,
            and because CLI output is refiltered as mentioned above, the final results
            can't be guaranteed.

        .. code-block:: shell-session

            $ cargo --color never --quiet search --limit 100 python
            python = "0.0.0"                  # Python.
            pyo3-asyncio = "0.16.0"           # PyO3 utilities for Python's Asyncio
            pyo3-asyncio-macros = "0.16.0"    # Proc Macro Attributes for PyO3 Asyncio
            pyo3 = "0.16.4"                   # Bindings to Python interpreter
            pyenv-python = "0.4.0"            # A pyenv shim for python
            python-launcher = "1.0.0"         # The Python launcher for Unix
            py-spy = "0.3.11"                 # Sampling profiler for Python programs
            python_mixin = "0.0.0"            # Use Python to generate your Rust, right…
            pyflow = "0.3.1"                  # A modern Python dependency manager
            pypackage = "0.0.3"               # A modern Python dependency manager
            ... and 1664 crates more (use --limit N to see more)
        """
        output = self.run_cli("search", "--limit", "100", query)

        for package_id, version, description in self._SEARCH_REGEXP.findall(output):
            yield self.package(
                id=package_id,
                description=description,
                latest_version=version,
            )

    @version_not_implemented
    def install(self, package_id: str, version: str | None = None) -> str:
        """Install one package.

        .. code-block:: shell-session

            $ cargo --color never install bore-cli
              Updating crates.io index
            Downloaded bore-cli v0.4.0
            Downloaded 1 crate (20.9 KB) in 0.26s
            Installing bore-cli v0.4.0
            Downloaded serde_derive v1.0.137
            Downloaded unicode-xid v0.2.3
            Downloaded clap_lex v0.2.0
            (...)
            Compiling bore-cli v0.4.0
              Finished release [optimized] target(s) in 1m 06s
             Replacing /home/mawoka/.cargo/bin/bore
              Replaced `bore-cli v0.2.3` with `bore-cli v0.4.0` (executable `bore`)
        """
        return self.run_cli("install", package_id)

    def remove(self, package_id: str) -> str:
        """Remove one package.

        .. code-block:: shell-session

            $ cargo --color never uninstall bore-cli
                Removing /Users/me/.cargo/bin/bore
        """
        return self.run_cli("uninstall", package_id)
