from ._tagger import VersionTagger
from .metadata.meta import version
from .path.filetools import write, read
import re, platform


class Versioner:
    """
    Advanced version management utility for Python modules.

    This class provides functionality to inspect, update, and maintain version
    identifiers within a Python package or module. It follows Python’s standard
    versioning conventions using either `__version__` or `VERSION` variables.

    Typical usage:
    --------------
    >>> ver = Versioner("mypackage")
    >>> print(ver.version)		  # '1.0.0'
    >>> ver.bump_version("1.1.0")   # Updates __version__ in source
    >>> print(ver.version_info())   # Returns metadata about version line
    """

    def __init__(self, ModuleName: str) -> None:
        """
        Initialize the Versioner with the specified module.

        Parameters
        ----------
        ModuleName : str
                The name of the module or package whose version information
                is to be managed.

        Notes
        -----
        - Uses Modulex for importing and locating the module.
        - Extracts current version components (major, minor, patch).
        """

    def __init__(self, ModuleName: str) -> None:
        from .importer import Modulex

        self.modx = Modulex(ModuleName)
        self.VersionTagger = VersionTagger(self.modx.path())

        lines = read(self.modx.file, mode="rl")
        version_line = next(
            (
                line
                for line in lines
                if line.strip().startswith(("__version__", "VERSION"))
            ),
            None,
        )

        if version_line:
            self.version = version_line.split("=")[1].strip().strip("'\"")
        else:
            self.version = platform.python_version()

        self.major, self.minor, self.patch = self.version.split(".")[:3]

    def __str__(self):
        """
        Return the current version string.

        Returns
        -------
        str
                The module version (e.g., '1.0.0').
        """
        return self.version

    def __repr__(self):
        """
        Return a detailed representation of the Versioner instance.

        Returns
        -------
        str
                Representation string including major, minor, and patch numbers.
        """
        return f"<Versioner(version='{self.version}', major='{self.major}', minor='{self.minor}', patch='{self.patch}')>"

    def bump_version(self, version: str, variable: str = "dunder") -> None:
        """
        Add or update version variable in the module source code.

        This method modifies the module's source file to set or update the
        version variable. It supports both double-underscore (`__version__`)
        and constant (`VERSION`) naming conventions.

        Parameters
        ----------
        version : str
                New version string to set (e.g., '1.2.3', '0.9.0-beta').
        variable : str, optional
                Determines naming style:
                - 'dunder'   → __version__  (Python standard)
                - 'variable' → VERSION	  (alternative)
                Default is 'dunder'.

        Raises
        ------
        ValueError
                If `variable` is not one of ['dunder', 'variable'].
        FileNotFoundError
                If the module file cannot be located.
        PermissionError
                If file write access is denied.

        Notes
        -----
        - Existing version lines are replaced safely using regex.
        - New lines are appended if no version variable exists.
        - Uses `pmeX` for atomic write operations.
        """
        from .pymodex import pmeX

        if variable == "dunder":
            var = "__version__"
        elif variable == "variable":
            var = "VERSION"
        else:
            raise ValueError(
                f"Expected variable type 'dunder' or 'variable', got {variable}"
            )

        pme = pmeX(self.modx.name)
        content = read(self.modx.file)

        if self.variable == "VERSION":
            pattern = rf"^VERSION\s*=\s*[^\n]+$"
        else:
            pattern = rf"^__version__\s*=\s*[^\n]+$"

        new_line = f"{var} = '{version}'"

        if re.search(pattern, content, re.MULTILINE):
            new_content = re.sub(pattern, new_line, content, flags=re.MULTILINE)
            pme.dump_code(new_content, target=self.modx.file, rewrite=True)
        else:
            pme.dump_code(new_line, target=self.modx.file)

    def version_info(self):
        """
        Return metadata about the current version variable in the source.

        Scans the module file to locate the line defining `__version__` or
        `VERSION`, returning its line number and file path.

        Returns
        -------
        dict
                A dictionary containing:
                        - 'file': Path to the module file.
                        - 'current_version': Current version string.
                        - 'lineno': Line number of version definition (if found), else None.

        Example
        -------
        >>> ver = Versioner("mypackage")
        >>> ver.version_info()
        {'file': '/path/to/__init__.py', 'current_version': '1.0.0', 'lineno': 5}
        """
        lines = read(self.modx.file, mode="rl")
        lineno = None
        for i, line in enumerate(lines, 0):
            if line.strip().startswith("__version__") or line.strip().startswith(
                "VERSION"
            ):
                lineno = i
        return {
            "file": self.modx.file,
            "current_version": self.version,
            "lineno": lineno,
        }

    @property
    def line(self):
        lines = read(self.modx.file, mode="rl")
        lineno = self.version_info()["lineno"]
        try:
            return lines[lineno].strip()
        except (TypeError, IndexError):
            return None

    def exists(self):
        return self.line is not None

    @property
    def variable(self):
        """
        Identify which version variable exists in the module.

        Returns
        -------
        str or None
                'VERSION' if uppercase variable is defined,
                '__version__' if dunder variable exists,
                or None if neither exists.
        """
        if not self.exists():
            return None
        if self.line.startswith("VERSION"):
            return "VERSION"
        elif self.line.startswith("__version__"):
            return "__version__"

    def remove(self) -> None:
        """
        Remove all occurrences of the version variable from the module's source file.

        This method scans the module's main file (usually `__init__.py`) line by line
        and removes any line containing the version variable (`__version__` or `VERSION`),
        ignoring any trailing comments.

        Steps:
        1. Reads the module file line by line.
        2. Determines which version variable(s) exist.
        3. Removes all lines that define the version variable.
        4. Writes back the remaining lines to the file, preserving other content.

        Notes
        -----
        - Destructive operation: all version definitions are removed permanently.
        - Lines with comments after the variable are also removed.
        - If the file does not contain a version variable, no changes are made.
        - Uses line-based read/write to minimize memory usage.

        Raises
        ------
        FileNotFoundError
                If the module file does not exist.
        IOError / PermissionError
                If the file cannot be written due to permission issues.

        Example
        -------
        >>> ver = Versioner("mypackage")
        >>> ver.remove()  # Deletes all __version__ or VERSION lines from the module file
        """
        lines = read(self.modx.file, mode="rl")
        variable_name = self.variable

        if variable_name is None:
            # No version variable found, nothing to remove
            return

        # Regex to match the variable line, ignoring trailing comments and spaces
        pattern = re.compile(rf"^{variable_name}\s*=\s*['\"].*?['\"]\s*(#.*)?$")

        # Keep only lines that do NOT match the version variable
        new_lines = [line for line in lines if not pattern.match(line.strip())]

        write(self.modx.file, new_lines, mode="wl")
