#!/usr/bin/python3


from __future__ import annotations

import logging
from typing import Any

from slpkg.binaries.install import Packages
from slpkg.choose_packages import Choose
from slpkg.load_data import LoadData
from slpkg.package_validator import PackageValidator
from slpkg.repositories import Repositories
from slpkg.sbo.slackbuild import Slackbuilds
from slpkg.utilities import Utilities

logger = logging.getLogger(__name__)


class Reinstall:  # pylint: disable=[R0903]
    """Reinstall installed packages from their source repository."""

    def __init__(self, packages: list[str], options: dict[str, Any]) -> None:
        self.packages = packages
        self.options: dict[str, Any] = dict(options)
        self.options["option_reinstall"] = True

        self.repos = Repositories()
        self.utils = Utilities()
        self.load_data = LoadData()
        self.pkg_validator = PackageValidator()
        self.sbo_repos: set[str] = {self.repos.sbo_repo_name, self.repos.ponce_repo_name}

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def run(self) -> None:
        """Validate, detect repos, and reinstall all requested packages."""
        logger.info("Starting reinstall for: %s", ", ".join(self.packages))

        self.pkg_validator.is_package_installed(self.packages)

        explicit_repo: str = self.options.get("option_repository") or ""
        if explicit_repo:
            grouped: dict[str, list[str]] = {explicit_repo: list(self.packages)}
        else:
            grouped = self._group_by_repo(self.packages)

        for repo_name, pkg_list in grouped.items():
            logger.info("Reinstalling %d package(s) from '%s'.", len(pkg_list), repo_name)
            data = self.load_data.load(repo_name, message=not self.options.get("option_quiet"))

            pkg_list = self.utils.case_insensitive_pattern_matching(pkg_list, data, self.options)

            if self.options.get("option_select") or self.options.get("option_dialog"):
                pkg_list = Choose(self.options, repo_name).packages(data, pkg_list, "reinstall", ordered=True)
                if not pkg_list:
                    continue

            self.pkg_validator.is_package_exists(pkg_list, data)

            if repo_name in self.sbo_repos:
                Slackbuilds(repo_name, data, pkg_list, self.options, mode="install").execute()
            else:
                Packages(repo_name, data, pkg_list, self.options, mode="install").execute()

    # ------------------------------------------------------------------
    # Private helpers
    # ------------------------------------------------------------------

    def _detect_repo(self, pkg_name: str) -> str | None:
        """Return the repository name for an installed package by matching its tag.

        Args:
            pkg_name: Package name as installed (name only, not full filename).

        Returns:
            Repository name, or None if no match is found.
        """
        installed_file = self.utils.is_package_installed(pkg_name)
        if not installed_file:
            return None

        tag: str = self.utils.split_package(installed_file)["tag"]

        for repo_name, cfg in self.repos.repositories.items():
            if not cfg.get("enable"):
                continue
            repo_tag: str = cfg.get("repo_tag", "")
            if repo_tag and tag == repo_tag:
                return repo_name

        # No tag → Slackware official packages (slack repo).
        if not tag:
            return self.repos.slack_repo_name

        return None

    def _group_by_repo(self, packages: list[str]) -> dict[str, list[str]]:
        """Group packages by their detected repository.

        Packages whose repository cannot be detected are skipped with a warning.

        Args:
            packages: Package names to group.

        Returns:
            Ordered dict mapping repo_name → [pkg_name, ...].
        """
        grouped: dict[str, list[str]] = {}

        for pkg in packages:
            repo = self._detect_repo(pkg)
            if repo is None:
                logger.warning("Cannot detect repository for '%s'; skipping.", pkg)
                print(f"Warning: cannot detect repository for '{pkg}' — skipping.")
                continue
            grouped.setdefault(repo, []).append(pkg)
            logger.debug("Package '%s' → repo '%s'.", pkg, repo)

        return grouped
