"""Go compiler with mirror acceleration support and failover."""

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

from loguru import logger

from multi_lang_build.compiler.base import CompilerBase, BuildResult, CompilerInfo
from multi_lang_build.mirror.config import (
    apply_mirror_environment,
    DEFAULT_GO_MIRROR,
    MIRROR_CONFIGS,
)
from multi_lang_build.compiler.go_support import (
    GoMirrorFailover,
    GoBinaryBuilder,
    GoModuleBuilder,
    GoTester,
    GoEnvironment,
    GoCleaner,
)


class GoCompiler(CompilerBase):
    """Compiler for Go projects with module support and mirror failover."""

    NAME: Final[str] = "go"
    DEFAULT_MIRROR: Final[str] = DEFAULT_GO_MIRROR

    def __init__(
        self,
        go_path: str | None = None,
        mirror: str | None = None,
    ):
        """Initialize Go compiler.

        Args:
            go_path: Optional path to Go executable
            mirror: Mirror configuration name (e.g., "go", "go_qiniu", "go_vip")
        """
        self._go_path = go_path
        self._mirror = mirror
        self._version_cache: str | None = None
        self._mirror_failover = GoMirrorFailover(mirror)

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

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

        go_executable = self._get_executable_path()

        try:
            result = subprocess.run(
                [go_executable, "version"],
                capture_output=True,
                text=True,
                timeout=10,
            )
            version_output = result.stdout.strip()
            if "version go" in version_output:
                self._version_cache = version_output.split("version go")[1].split()[0]
            else:
                self._version_cache = "unknown"
        except Exception:
            self._version_cache = "unknown"

        return self._version_cache

    @property
    def supported_mirrors(self) -> list[str]:
        """Get list of supported mirror configurations."""
        return ["go", "go_qiniu", "go_vip"]

    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 _get_executable_path(self) -> str:
        """Get the path to the Go executable."""
        if self._go_path:
            return shutil.which(self._go_path) or self._go_path

        go_executable = shutil.which("go")
        if not go_executable:
            raise RuntimeError("Go executable not found in PATH")

        return go_executable

    def _prepare_environment(self, environment: dict[str, str] | None = None) -> dict[str, str]:
        """Prepare environment with GOMODCACHE and GOCACHE set."""
        return GoEnvironment.prepare(environment)

    def _execute_with_mirror_failover(
        self,
        command: list[str],
        source_dir: Path,
        output_dir: Path,
        environment: dict[str, str],
        stream_output: bool,
    ) -> BuildResult:
        """Execute build command with mirror failover."""
        def run_build(cmd, src, out, env, stream):
            return self._run_build(cmd, src, out, environment=env, stream_output=stream)

        return self._mirror_failover.try_build_with_fallback(
            command, source_dir, output_dir, environment, stream_output, run_build
        )

    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 Go build process.

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

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

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

        cwd = Path.cwd().resolve()
        logger.info(f"当前工作目录: {cwd}")

        env = self._prepare_environment(environment)

        # Download dependencies if go.mod exists
        go_mod = source_dir / "go.mod"
        if go_mod.exists():
            logger.info("⬇️  下载Go依赖...")

            # 显示当前镜像配置
            if mirror_enabled:
                if self._mirror:
                    mirror_key = self._mirror
                else:
                    mirror_key = "go_qiniu"  # 默认使用第一个
                mirror_info = MIRROR_CONFIGS.get(mirror_key, {})
                mirror_name = mirror_info.get("name", mirror_key)
                mirror_url = mirror_info.get("proxy", "default")
                logger.info(f"🔗 Go代理: {mirror_name} ({mirror_url})")

            deps_result = self._execute_with_mirror_failover(
                [go_executable, "mod", "download"],
                source_dir, output_dir, env, stream_output,
            )
            logger.info("✅ 依赖下载完成")

            if not deps_result["success"]:
                return deps_result

        # Build the project
        build_args = [go_executable, "build", "-o", str(output_dir)]

        if extra_args:
            build_args.extend(extra_args)

        logger.info("🔨 开始构建...")
        logger.info(f"构建命令: {' '.join(build_args)}")

        if mirror_enabled and self._mirror is None:
            build_result = self._execute_with_mirror_failover(
                build_args, source_dir, output_dir, env, stream_output
            )
        else:
            if mirror_enabled:
                env = apply_mirror_environment(self._mirror or "go", env)
            build_result = self._run_build(
                build_args, source_dir, output_dir, environment=env, stream_output=stream_output
            )

        if build_result["success"]:
            logger.info("✅ 构建成功")

        return build_result

    def build_binary(
        self,
        source_dir: Path,
        output_path: Path,
        *,
        target: str | Path | None = None,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        ldflags: str | None = None,
        tags: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Build a specific Go binary.

        Args:
            source_dir: Source code directory
            output_path: Output path for the binary
            target: Build target file or directory
            environment: Additional environment variables
            mirror_enabled: Whether to use Go proxy mirror
            ldflags: Linker flags
            tags: Build tags
            stream_output: Whether to stream output in real-time

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

        source_dir = self._validate_directory(source_dir, create_if_not_exists=False)
        output_path.parent.mkdir(parents=True, exist_ok=True)

        cwd = Path.cwd().resolve()
        logger.info(f"当前工作目录: {cwd}")

        env = self._prepare_environment(environment)

        def run_build(cmd, src, out, env_copy):
            return self._run_build(cmd, src, out, environment=env_copy, stream_output=stream_output)

        if mirror_enabled and self._mirror is None:
            return GoBinaryBuilder.build_with_failover(
                go_executable, source_dir, output_path, env,
                ldflags, tags, target, run_build, stream_output
            )
        else:
            if mirror_enabled:
                env = apply_mirror_environment(self._mirror or "go", env)
            return GoBinaryBuilder.build_single(
                go_executable, source_dir, output_path, env,
                ldflags, tags, target, run_build, stream_output
            )

    def build_all(
        self,
        source_dir: Path,
        output_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        platform: str | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Build all binaries in the project.

        Args:
            source_dir: Source code directory
            output_dir: Output directory for all binaries
            environment: Additional environment variables
            mirror_enabled: Whether to use Go proxy mirror
            platform: Target platform (e.g., "linux/amd64")
            stream_output: Whether to stream output in real-time

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

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

        cwd = Path.cwd().resolve()
        logger.info(f"当前工作目录: {cwd}")

        env = self._prepare_environment(environment)

        def run_build(cmd, src, out, env_copy):
            return self._run_build(cmd, src, out, environment=env_copy, stream_output=stream_output)

        if mirror_enabled and self._mirror is None:
            build_result = self._execute_with_mirror_failover(
                [go_executable, "build", "-o", str(output_dir), "./..."],
                source_dir, output_dir, env, stream_output
            )
        else:
            if mirror_enabled:
                env = apply_mirror_environment(self._mirror or "go", env)
            build_result = GoModuleBuilder.build_all(
                go_executable, source_dir, output_dir, env, platform, run_build, stream_output
            )

        return build_result

    def run_tests(
        self,
        source_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        verbose: bool = True,
        race: bool = False,
        stream_output: bool = True,
    ) -> BuildResult:
        """Run Go tests.

        Args:
            source_dir: Source code directory
            environment: Additional environment variables
            mirror_enabled: Whether to use Go proxy mirror
            verbose: Enable verbose test output
            race: Enable race detector
            stream_output: Whether to stream output in real-time

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

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

        cwd = Path.cwd().resolve()
        logger.info(f"当前工作目录: {cwd}")

        env = self._prepare_environment(environment)

        if mirror_enabled:
            env = apply_mirror_environment(self._mirror or "go", env)

        def run_test(cmd, src, out, env_copy):
            return self._run_build(cmd, src, out, environment=env_copy, stream_output=stream_output)

        return GoTester.run(
            go_executable, source_dir, env, verbose, race, stream_output, run_test
        )

    def tidy_modules(
        self,
        source_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        mirror_enabled: bool = True,
        stream_output: bool = True,
    ) -> BuildResult:
        """Run go mod tidy to clean up dependencies.

        Args:
            source_dir: Source code directory
            environment: Additional environment variables
            mirror_enabled: Whether to use Go proxy mirror
            stream_output: Whether to stream output in real-time

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

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

        cwd = Path.cwd().resolve()
        logger.info(f"当前工作目录: {cwd}")

        env = self._prepare_environment(environment)

        def run_build(cmd, src, out, env_copy):
            return self._run_build(cmd, src, out, environment=env_copy, stream_output=stream_output)

        if mirror_enabled and self._mirror is None:
            return self._execute_with_mirror_failover(
                [go_executable, "mod", "tidy"],
                source_dir, source_dir, env, stream_output
            )
        else:
            if mirror_enabled:
                env = apply_mirror_environment(self._mirror or "go", env)
            return GoModuleBuilder.tidy(go_executable, source_dir, env, run_build, stream_output)

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

        Args:
            directory: Directory to clean

        Returns:
            True if successful, False otherwise.
        """
        directory = self._validate_directory(directory, create_if_not_exists=False)
        return GoCleaner.clean(directory)
