"""PNPM compiler with mirror acceleration support."""

from pathlib import Path
from typing import Final
import shutil
import os

from multi_lang_build.compiler.base import CompilerBase, BuildResult, CompilerInfo
from multi_lang_build.mirror.config import apply_mirror_environment
from multi_lang_build.compiler.pnpm_support import PnpmProject, PnpmExecutor


class PnpmCompiler(CompilerBase):
    """Compiler for pnpm-based frontend projects."""

    NAME: Final[str] = "pnpm"
    DEFAULT_MIRROR: Final[str] = "https://registry.npmmirror.com"

    def __init__(self, pnpm_path: str | None = None) -> None:
        """Initialize the PNPM compiler.

        Args:
            pnpm_path: Optional path to pnpm executable. If None, uses system PATH.
        """
        self._pnpm_path = pnpm_path
        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 pnpm version."""
        if self._version_cache:
            return self._version_cache

        pnpm_executable = self._get_executable_path()
        import subprocess

        try:
            result = subprocess.run(
                [pnpm_executable, "--version"],
                capture_output=True,
                text=True,
                timeout=10,
            )
            self._version_cache = result.stdout.strip()
        except Exception:
            self._version_cache = "unknown"

        return self._version_cache

    @property
    def supported_mirrors(self) -> list[str]:
        """Get list of supported mirror configurations."""
        return ["npm", "pnpm", "yarn"]

    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 find_project_root(self, start_path: Path) -> Path | None:
        """Find the frontend project root directory containing package.json."""
        return PnpmProject.find_root(start_path)

    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 pnpm build process with auto-detection of project root.

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

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

        # Auto-detect project root
        project_root = self.find_project_root(source_dir)

        if project_root is None:
            return BuildResult(
                success=False,
                return_code=-1,
                stdout="",
                stderr=f"package.json not found in {source_dir} or its parent/sub directories",
                output_path=None,
                duration_seconds=0.0,
            )

        # Validate directories
        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("pnpm", env)
            env = apply_mirror_environment("npm", env)

        # Install dependencies in project root
        install_result = PnpmExecutor.execute(
            [pnpm_executable, "install"],
            project_root,
            env,
            stream_output=stream_output,
        )

        if not install_result["success"]:
            return install_result

        # Run build
        build_args = [pnpm_executable, "build"]
        if extra_args:
            build_args.extend(extra_args)

        return PnpmExecutor.execute(
            build_args,
            project_root,
            env,
            stream_output=stream_output,
        )

    def install_dependencies(
        self,
        source_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        production: bool = False,
    ) -> BuildResult:
        """Install dependencies using pnpm with auto-detection of project root.

        Args:
            source_dir: Source code directory or subdirectory
            environment: Additional environment variables
            mirror_enabled: Whether to use mirror acceleration
            production: Install only production dependencies

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

        project_root = self.find_project_root(source_dir)

        if project_root is None:
            return BuildResult(
                success=False,
                return_code=-1,
                stdout="",
                stderr=f"package.json not found in {source_dir} or its parent/sub directories",
                output_path=None,
                duration_seconds=0.0,
            )

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

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

        command = [pnpm_executable, "install"]
        if production:
            command.append("--prod")

        return PnpmExecutor.execute(
            command,
            project_root,
            env,
            stream_output=True,
        )

    def run_script(
        self,
        source_dir: Path,
        script_name: str,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
    ) -> BuildResult:
        """Run a specific npm script with auto-detection of project root.

        Args:
            source_dir: Source code directory or subdirectory
            script_name: Name of the script to run
            environment: Additional environment variables
            mirror_enabled: Whether to use mirror acceleration

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

        project_root = self.find_project_root(source_dir)

        if project_root is None:
            return BuildResult(
                success=False,
                return_code=-1,
                stdout="",
                stderr=f"package.json not found in {source_dir} or its parent/sub directories",
                output_path=None,
                duration_seconds=0.0,
            )

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

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

        return PnpmExecutor.execute(
            [pnpm_executable, "run", script_name],
            project_root,
            env,
        )

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

        Args:
            directory: Directory to clean

        Returns:
            True if successful, False otherwise.
        """
        import shutil

        try:
            directory = self._validate_directory(directory, create_if_not_exists=False)

            # Remove node_modules
            node_modules = directory / "node_modules"
            if node_modules.exists():
                shutil.rmtree(node_modules)

            # Remove pnpm-lock.yaml
            lock_file = directory / "pnpm-lock.yaml"
            if lock_file.exists():
                lock_file.unlink()

            # Remove .pnpm-store if exists
            pnpm_store = directory / ".pnpm-store"
            if pnpm_store.exists():
                shutil.rmtree(pnpm_store)

            return True

        except Exception:
            return False

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

        pnpm_path = shutil.which("pnpm")
        if pnpm_path is None:
            raise RuntimeError("pnpm not found in PATH. Please install pnpm or provide pnpm_path.")

        return pnpm_path

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

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

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


def main() -> None:
    """PNPM compiler CLI entry point."""
    import argparse
    import sys

    parser = argparse.ArgumentParser(description="PNPM Build Compiler")
    parser.add_argument("source_dir", type=Path, help="Source directory")
    parser.add_argument("-o", "--output", type=Path, required=True, help="Output directory")
    parser.add_argument("--mirror", action="store_true", default=True, help="Enable mirror acceleration")
    parser.add_argument("--no-mirror", dest="mirror", action="store_false", help="Disable mirror acceleration")
    parser.add_argument("--script", type=str, help="Run specific npm script instead of build")
    parser.add_argument("--install", action="store_true", help="Install dependencies only")

    args = parser.parse_args()

    compiler = PnpmCompiler()

    if args.script:
        result = compiler.run_script(
            args.source_dir,
            args.script,
            mirror_enabled=args.mirror,
        )
    elif args.install:
        result = compiler.install_dependencies(
            args.source_dir,
            mirror_enabled=args.mirror,
        )
    else:
        result = compiler.build(
            args.source_dir,
            args.output,
            mirror_enabled=args.mirror,
        )

    sys.exit(0 if result["success"] else 1)
