#!/usr/bin/python3


from __future__ import annotations

import logging
import shutil
import time
from pathlib import Path
from typing import Any

from slpkg.config import config_load
from slpkg.history_logger import HistoryLogger
from slpkg.multi_process import MultiProcess
from slpkg.package_validator import PackageValidator
from slpkg.utilities import Utilities

logger = logging.getLogger(__name__)


class LocalInstall:  # pylint: disable=[R0902,R0903]
    """Install one or more local Slackware package files directly."""

    def __init__(self, options: dict[str, Any], packages: list[str]) -> None:
        logger.debug("Initializing LocalInstall with packages: %s, options: %s", packages, options)
        self.packages = [Path(p) for p in packages]
        self.options = options

        self.installpkg = config_load.local_installpkg
        self.reinstall = config_load.reinstall
        self.package_type = tuple(config_load.package_type)
        self.process_log_file = config_load.process_log_file
        self.green = config_load.green
        self.yellow = config_load.yellow
        self.red = config_load.red
        self.endc = config_load.endc

        self.utils = Utilities()
        self.pkg_validator = PackageValidator()
        self.multi_proc = MultiProcess(options)
        self.history_logger = HistoryLogger()

        self.option_yes: bool = options.get("option_yes", False)
        self.option_reinstall: bool = options.get("option_reinstall", False)
        self.option_extra_args: str = options.get("option_extra_args", "") or ""

    def _validate(self) -> list[Path]:
        """Return only valid, existing Slackware package files.

        Prints an error for each invalid or missing path.

        Returns:
            list[Path]: Validated package file paths.
        """
        valid: list[Path] = []
        for path in self.packages:
            if not path.is_file():
                print(f"{self.red}File not found:{self.endc} {path}")
                logger.warning("File not found: '%s'", path)
                continue
            if path.suffix not in self.package_type:
                print(f"{self.red}Not a valid Slackware package:{self.endc} {path.name}")
                logger.warning("Invalid package extension '%s' for file: '%s'", path.suffix, path)
                continue
            valid.append(path)
        return valid

    def _show_packages(self, valid: list[Path]) -> None:
        """Print a summary of packages about to be installed.

        Args:
            valid (list[Path]): Validated package paths.
        """
        print("Local packages to install:\n")
        has_blacklisted = False
        for path in valid:
            name = self.utils.split_package(path.stem)["name"]
            already = self.utils.is_package_installed(name)

            status = self.pkg_validator.get_blacklist_status(name)
            if status:
                has_blacklisted = True

            if self.option_reinstall:
                label = f"{self.yellow}reinstall{self.endc}"
            elif already:
                label = f"{self.yellow}upgrade{self.endc}"
            else:
                label = f"{self.green}install{self.endc}"
            print(f"  [{label}] {path.name}{status}")

        if has_blacklisted:
            print(f"\n{self.yellow}Warning: One or more packages are in the blacklist.{self.endc}")
        print()

    def run(self) -> None:
        """Validate, confirm, and install local package files."""
        valid = self._validate()
        if not valid:
            return

        self._show_packages(valid)

        if not self.option_yes:
            try:
                answer = input("Do you want to continue? [y/N] ")
            except (KeyboardInterrupt, EOFError):
                print()
                return
            if answer.strip().lower() != "y":
                return

        self.process_log_file.unlink(missing_ok=True)
        n = len(valid)
        self.multi_proc.setup_counter(n)
        print(f"\nProcessing {n} package{'s' if n != 1 else ''}:")
        print(config_load.sep_line * (shutil.get_terminal_size().columns - 1))

        start: float = time.time()
        for path in valid:
            name = self.utils.split_package(path.stem)["name"]
            already = self.utils.is_package_installed(name)

            extra = f" {self.option_extra_args}" if self.option_extra_args else ""

            if self.option_reinstall:
                command = f"{self.reinstall}{extra} {path}"
                history_action = "reinstall"
                progress_message = "Reinstalling"
            elif already:
                command = f"{self.installpkg}{extra} {path}"
                history_action = "upgrade"
                progress_message = "Upgrading"
            else:
                command = f"{self.installpkg}{extra} {path}"
                history_action = "install"
                progress_message = "Installing"

            logger.debug("Running: %s", command)
            succeeded: bool = self.multi_proc.process_and_log(command, path.name, progress_message)
            if succeeded:
                self.history_logger.write(history_action, name, path.name, "local")
                logger.info("Package '%s' %sed successfully.", name, history_action)

        elapsed: float = time.time() - start
        self.utils.finished_time(elapsed)
