"""Python compiler with mirror acceleration support."""

from pathlib import Path
from typing import Final
import shutil
import subprocess
import sys

from multi_lang_build.compiler.base import CompilerBase, BuildResult, CompilerInfo
from multi_lang_build.mirror.config import (
    apply_mirror_environment,
    DEFAULT_PIP_MIRROR,
)
from multi_lang_build.compiler.python_support import (
    BuildSystemDetector,
    DependencyInstaller,
    VenvManager,
    PythonCleaner,
)


class PythonCompiler(CompilerBase):
    """Compiler for Python projects with pip and poetry support."""

    NAME: Final[str] = "python"
    DEFAULT_MIRROR: Final[str] = DEFAULT_PIP_MIRROR

    def __init__(
        self,
        python_path: str | None = None,
        mirror: str | None = None,
    ) -> None:
        """Initialize the Python compiler.

        Args:
            python_path: Optional path to python executable. If None, uses sys.executable.
            mirror: Mirror configuration name (e.g., "pip", "pip_aliyun", "pip_douban", "pip_huawei").
                    Defaults to DEFAULT_PIP_MIRROR (Tsinghua).
        """
        self._python_path = python_path
        self._mirror = mirror
        self._version_cache: str | None = None

    @property
    def name(self) -> str:
        """Get the compiler name."""
        return self.NAME

    @property
    def version(self) -> str:
        """Get the Python version."""
        if self._version_cache:
            return self._version_cache

        python_executable = self._get_executable_path()

        try:
            result = subprocess.run(
                [python_executable, "--version"],
                capture_output=True,
                text=True,
                timeout=10,
            )
            output = result.stdout.strip() or result.stderr.strip()
            self._version_cache = output.replace("Python ", "")
        except Exception:
            self._version_cache = sys.version.split()[0]

        return self._version_cache

    @property
    def supported_mirrors(self) -> list[str]:
        """Get list of supported mirror configurations."""
        return ["pip", "pip_aliyun", "pip_douban", "pip_huawei"]

    @property
    def current_mirror(self) -> str:
        """Get the current mirror configuration name."""
        return self._mirror or "pip"

    def get_info(self) -> CompilerInfo:
        """Get compiler information."""
        return {
            "name": self.name,
            "version": self.version,
            "supported_mirrors": self.supported_mirrors,
            "default_mirror": self.DEFAULT_MIRROR,
            "executable": self._get_executable_path(),
        }

    def set_mirror(self, mirror: str) -> None:
        """Set the mirror configuration.

        Args:
            mirror: Mirror configuration name (e.g., "pip", "pip_aliyun", "pip_douban", "pip_huawei")
        """
        self._mirror = mirror

    def build(
        self,
        source_dir: Path,
        output_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        extra_args: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Execute the Python build process.

        Args:
            source_dir: Source code directory
            output_dir: Build output directory
            environment: Additional environment variables
            mirror_enabled: Whether to use PyPI mirror
            extra_args: Additional arguments to pass to build command
            stream_output: Whether to stream output in real-time (default: True)

        Returns:
            BuildResult containing success status and output information.
        """
        python_executable = self._get_executable_path()

        # Validate directories
        source_dir = self._validate_directory(source_dir, create_if_not_exists=False)
        output_dir = self._validate_directory(output_dir, create_if_not_exists=True)

        # Prepare environment
        env = environment.copy() if environment else {}

        if mirror_enabled:
            env = apply_mirror_environment("python", env)
            env = apply_mirror_environment("pip", env)

        # Determine build system
        build_system = BuildSystemDetector.detect(source_dir)

        if build_system == "poetry":
            return self._build_with_poetry(
                python_executable, source_dir, output_dir, env, extra_args, stream_output
            )
        elif build_system == "setuptools":
            return self._build_with_setuptools(
                python_executable, source_dir, output_dir, env, extra_args, stream_output
            )
        elif build_system == "pdm":
            return self._build_with_pdm(
                python_executable, source_dir, output_dir, env, extra_args, stream_output
            )
        else:
            return BuildResult(
                success=False,
                return_code=-1,
                stdout="",
                stderr=f"No supported build system found in {source_dir}. "
                       "Expected pyproject.toml, setup.py, or setup.cfg",
                output_path=None,
                duration_seconds=0.0,
            )

    def _build_with_poetry(
        self,
        python_executable: str,
        source_dir: Path,
        output_dir: Path,
        env: dict[str, str],
        extra_args: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Build using Poetry."""
        # Install dependencies
        install_cmd = [python_executable, "-m", "poetry", "install"]

        install_result = self._run_build(
            install_cmd,
            source_dir,
            output_dir,
            environment=env,
            stream_output=stream_output,
        )

        if not install_result["success"]:
            return install_result

        # Build package
        build_cmd = [python_executable, "-m", "poetry", "build"]

        if extra_args:
            build_cmd.extend(extra_args)

        return self._run_build(
            build_cmd,
            source_dir,
            output_dir,
            environment=env,
            stream_output=stream_output,
        )

    def _build_with_setuptools(
        self,
        python_executable: str,
        source_dir: Path,
        output_dir: Path,
        env: dict[str, str],
        extra_args: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Build using setuptools."""
        # Install build dependencies first
        if (source_dir / "pyproject.toml").exists():
            # Use build module for modern setuptools
            build_cmd = [python_executable, "-m", "build", "--outdir", str(output_dir)]
        else:
            # Legacy setup.py
            build_cmd = [python_executable, "setup.py", "bdist_wheel", "--dist-dir", str(output_dir)]

        if extra_args:
            build_cmd.extend(extra_args)

        return self._run_build(
            build_cmd,
            source_dir,
            output_dir,
            environment=env,
            stream_output=stream_output,
        )

    def _build_with_pdm(
        self,
        python_executable: str,
        source_dir: Path,
        output_dir: Path,
        env: dict[str, str],
        extra_args: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Build using PDM."""
        # Build with PDM
        build_cmd = [python_executable, "-m", "pdm", "build"]

        if extra_args:
            build_cmd.extend(extra_args)

        return self._run_build(
            build_cmd,
            source_dir,
            output_dir,
            environment=env,
            stream_output=stream_output,
        )

    def install_dependencies(
        self,
        source_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        dev: bool = False,
        poetry: bool = False,
    ) -> BuildResult:
        """Install Python dependencies.

        Args:
            source_dir: Source code directory
            environment: Additional environment variables
            mirror_enabled: Whether to use PyPI mirror
            dev: Include development dependencies
            poetry: Force using poetry

        Returns:
            BuildResult containing success status and output information.
        """
        python_executable = self._get_executable_path()

        source_dir = self._validate_directory(source_dir, create_if_not_exists=False)

        env = environment.copy() if environment else {}

        if mirror_enabled:
            env = apply_mirror_environment("pip", env)

        # Check for poetry first
        if poetry or BuildSystemDetector.is_poetry(source_dir):
            return DependencyInstaller.install_poetry(
                python_executable, source_dir, env, dev, True
            )

        # Use pip
        return DependencyInstaller.install_pip(
            python_executable, source_dir, env, dev, True
        )

    def create_venv(
        self,
        directory: Path,
        *,
        python_path: str | None = None,
        mirror_enabled: bool = True,
    ) -> BuildResult:
        """Create a virtual environment.

        Args:
            directory: Directory to create the venv in
            python_path: Python interpreter to use
            mirror_enabled: Whether to configure pip mirror

        Returns:
            BuildResult containing success status and output information.
        """
        return VenvManager.create(directory, python_path, mirror_enabled)

    def run_script(
        self,
        source_dir: Path,
        script_name: str,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
    ) -> BuildResult:
        """Run a Python script or module.

        Args:
            source_dir: Source code directory
            script_name: Script or module name to run
            environment: Additional environment variables
            mirror_enabled: Whether to use PyPI mirror

        Returns:
            BuildResult containing success status and output information.
        """
        python_executable = self._get_executable_path()

        source_dir = self._validate_directory(source_dir, create_if_not_exists=False)

        env = environment.copy() if environment else {}

        if mirror_enabled:
            env = apply_mirror_environment("pip", env)

        return self._run_build(
            [python_executable, "-m", script_name],
            source_dir,
            source_dir,
            environment=env,
        )

    def clean(self, directory: Path) -> bool:
        """Clean Python build artifacts in the specified directory.

        Args:
            directory: Directory to clean

        Returns:
            True if successful, False otherwise.
        """
        return PythonCleaner.clean(directory)

    def _get_executable_path(self) -> str:
        """Get the python executable path."""
        if self._python_path:
            return self._python_path

        python_path = shutil.which("python3") or shutil.which("python")
        if python_path is None:
            return sys.executable

        return python_path

    @staticmethod
    def create(source_dir: Path, *, mirror_enabled: bool = True) -> "PythonCompiler":
        """Factory method to create a PythonCompiler instance.

        Args:
            source_dir: Source directory for the project
            mirror_enabled: Whether to enable mirror acceleration by default

        Returns:
            Configured PythonCompiler instance
        """
        compiler = PythonCompiler()
        return compiler

