import platform
import re

from packaging.version import Version
from pathlib import Path
from typing import NamedTuple

import wox.subprocessor as subprocessor

from wox.logger.logger_setup import wox_logger
from wox.utils import normalize_python_version
from wox.wox_exceptions import NotPythonVenvWarning


class PythonInterpreter(NamedTuple):
    """
        Parameters
        ----------
        version: pachaking.version.Version

        path: pathlib.Path
    """
    version: Version
    path: Path

def python_discovery(env, python_cmd = None):
    """
        Discovers and creates a Python interpreter with version and path.

        Parameters
        ----------
        env : str
            Name of the virtual environment
        python_cmd : str, default None
            Path to a Python interpreter provided by python_build from .toml file.

        Returns
        -------
        PythonInterpreter
            Python interpreter with its version and path.
    """

    if python_cmd is None:
        if env.startswith('py') and env[2] == '3':
            version = get_interpreter_version(env)
            wox_logger.debug(f'Looking for an interpreter version {version}.')

            match platform.system():
                case 'Windows':
                    python_cmd = 'py', f'-{version}'
                case 'Linux':
                    python_cmd = f'python{version}'
        else:
            wox_logger.debug('Looking for the system Python interpreter.')
            python_cmd = find_python_system()

    script = "import platform, sys; print(platform.python_version(), sys.executable)"

    python_information = subprocessor.run_command(
        f'{python_cmd} -c "{script}"',
        unsafe_command = True,
        return_output = True
    )

    version, path = python_information.split()

    wox_logger.debug(f"Interpreter found with version '{version}' on path '{path}'!")

    return PythonInterpreter(Version(version), Path(path))

def get_interpreter_version(env):
    """
        Extracts version from name of virtual environments following
        the nomenclature py<version> (e.g. py311).

        Parameters
        ----------
        env : str
            Name of the virtual environment.

        Returns
        -------
        str
            Python version using the major.minor scheme.
    """
    version_tag = env.lstrip('py')
    version = normalize_python_version(version_tag)

    return version

def find_python_system():
    """
        Finds the system Python interpreter.

        Returns
        -------
        str
            Command to start Python by default on the system.
    """
    match platform.system():
        case 'Windows':
            python_cmd = 'py'
        case 'Linux':
            python_cmd = 'python3'

    return python_cmd

def get_python_version_from_pyvenv_cfg(env_path):
    """
        Retrieves Python version of a virtual environment.

        Parameters
        ----------
        env_path : pathlib.Path
            Path to the virtual environment.

        Returns
        -------
        str
            Python version using the major.minor.fix scheme.
    """

    pyvenv_cfg = env_path / 'pyvenv.cfg'
    if pyvenv_cfg.exists():
        with open(pyvenv_cfg) as file:
            for line in file:
                if line.startswith('version'):
                    version = line.split('=')[1].strip()
                    version = re.findall(r'\d+', version)
                    return '.'.join(version[:3])
    else:
        raise NotPythonVenvWarning(env_path)

def locate_venv_python_exe(env_path):
    """
        Locates the Python interpreter of a virtual environment.

        Parameters
        ----------
        env_path : pathlib.Path
            Path to the virtual environment.

        Returns
        -------
        PythonInterpreter
            Python interpreter with its version and path.
    """

    version = get_python_version_from_pyvenv_cfg(env_path)

    python_paths = [
        env_path / 'Scripts' / 'python.exe',
        env_path / 'Scripts' / 'python',
        env_path / 'bin' / 'python',
        env_path / 'bin' / 'python3',
        env_path / 'bin' / 'python.exe',
        env_path / 'bin' / 'python3.exe',
    ]

    for path in python_paths:
        if path.exists():
            python_path = path

            wox_logger.debug(
                f"Interpreter located for venv '{env_path.name}' with version {version}"
                f" successfully at '{python_path}'!"
            )

            return PythonInterpreter(Version(version), python_path)
