import subprocess
from typing import Union
from ._cache._cache_data import DataCache

# Initialize cache handler and prepare cache file
dc = DataCache()
dc.init()  # Prepares the cache file (creates if not exists, validates format)
load = dc.load()  # Load existing cached data


class pkgs:
    """
    A utility class for managing Python package information.

    Provides static methods to list, search, and check for installed packages,
    using a cache mechanism to improve performance.
    """

    @staticmethod
    def listall() -> list:
        """
        Retrieve all installed packages with their versions.

        Uses cached data if available, otherwise fetches fresh data using pip,
        caches it, and then returns the results.

        Returns:
            list: A list of tuples where each tuple contains (package_name, version)
        """
        results = []

        # Check if cache is empty
        if not load:
            # Execute 'pip list' command to get all installed packages
            pkgs = subprocess.run(["pip", "list"], capture_output=True, text=True)
            # Skip header lines (first 2 lines) and process the rest
            pkgs = pkgs.stdout.splitlines()[2:]

            # Extract package name and version from each line
            for pkg in pkgs:
                results.append((pkg.split()[0], pkg.split()[1]))

            # Cache the results for future use
            dc.cache(results)
        else:
            # Use cached data
            for pkg in load:
                results.append((pkg[0], pkg[1]))

        return results

    @staticmethod
    def getpkg(name_or_version: str) -> Union[list, None]:
        """
        Search for installed packages by name or version (case-insensitive partial matching).

        This method performs a comprehensive search through all installed packages,
        looking for matches in both package names and versions. The search is
        case-insensitive and supports partial matching.

        Args:
            name_or_version (str): The search name to match against package names or versions.
                       Can be a full name, partial name, version number, or partial version.

        Returns:
            Union[list, None]:
                - list: A list of tuples containing matching packages in the format
                       [(package_name, version), ...]
                - None: If no packages match the search query

        Examples:
            >>> pkgs.getpkg("django")
            [('django', '4.2.1')]

            >>> pkgs.getpkg("requests")
            [('requests', '2.31.0')]

            >>> pkgs.getpkg("2.3")
            [('requests', '2.31.0'), ('numpy', '1.23.4')]

            >>> pkgs.getpkg("nonexistent")
            None

        Notes:
            - Uses cached package data for performance
            - Search is case-insensitive (converts both query and package names to lowercase)
            - Supports partial matching (e.g., "dj" will match "django")
            - Returns None instead of empty list for clearer intent
        """
        name = name_or_version.lower()
        results = []

        # Search through all installed packages
        for pkg_name, version in pkgs.listall():
            # Case-insensitive partial matching in both name and version
            if name in pkg_name.lower() or name in version:
                results.append((pkg_name, version))

        return results[0] if len(results) == 1 else results if results else None

    @staticmethod
    def update():
        """
        Clear the package cache.

        This forces the next call to listall() to fetch fresh data from pip
        instead of using cached data.
        """
        dc.clear()  # Clear the cache file
