"""Base compiler interface and common types."""

from abc import ABC, abstractmethod
from typing import TypeAlias, TypedDict, NotRequired
from pathlib import Path
import time
import subprocess
import sys


class BuildConfig(TypedDict, total=False):
    """Build configuration for compiler."""
    source_dir: Path
    output_dir: Path
    environment: dict[str, str]
    mirror_enabled: bool
    mirror_region: str
    extra_args: list[str]


class BuildResult(TypedDict):
    """Result of a build operation."""
    success: bool
    return_code: int
    stdout: str
    stderr: str
    output_path: Path | None
    duration_seconds: float


class CompilerInfo(TypedDict):
    """Information about a compiler."""
    name: str
    version: str
    supported_mirrors: list[str]
    default_mirror: str
    executable: str


class CompilerBase(ABC):
    """Abstract base class for all language compilers."""

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

    @property
    @abstractmethod
    def version(self) -> str:
        """Get the compiler version."""
        ...

    @property
    @abstractmethod
    def supported_mirrors(self) -> list[str]:
        """Get list of supported mirror configurations."""
        ...

    @abstractmethod
    def get_info(self) -> CompilerInfo:
        """Get compiler information."""
        ...

    @abstractmethod
    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 build process.

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

        Returns:
            BuildResult containing success status and output information.
        """
        ...

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

        Args:
            directory: Directory to clean

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

    def _run_build(
        self,
        command: list[str],
        source_dir: Path,
        output_dir: Path,
        *,
        environment: dict[str, str] | None = None,
        extra_args: list[str] | None = None,
        stream_output: bool = True,
    ) -> BuildResult:
        """Execute a build command with timing and error handling.

        Args:
            command: Build command to execute
            source_dir: Source directory for the build
            output_dir: Output directory for build artifacts
            environment: Additional environment variables
            extra_args: Additional arguments to append to command
            stream_output: Whether to stream output in real-time (default: True)

        Returns:
            BuildResult with success status and output information.
        """
        full_command = command.copy()
        if extra_args:
            full_command.extend(extra_args)

        env = environment.copy() if environment else {}
        env.setdefault("PATH", "")

        start_time = time.perf_counter()

        if stream_output:
            return self._run_build_stream(
                full_command, source_dir, output_dir, env, start_time
            )
        else:
            return self._run_build_capture(
                full_command, source_dir, output_dir, env, start_time
            )

    def _run_build_stream(
        self,
        command: list[str],
        source_dir: Path,
        output_dir: Path,
        env: dict[str, str],
        start_time: float,
    ) -> BuildResult:
        """Execute build command with real-time output streaming.

        Args:
            command: Build command to execute
            source_dir: Source directory for the build
            output_dir: Output directory for build artifacts
            env: Environment variables
            start_time: Start time for duration calculation

        Returns:
            BuildResult with success status and output information.
        """
        stdout_buffer = []
        stderr_buffer = []

        try:
            process = subprocess.Popen(
                command,
                cwd=source_dir,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                env=env,
            )

            # Read stdout in real-time
            for line in process.stdout:
                line = line.rstrip('\n\r')
                stdout_buffer.append(line)
                print(line)
                sys.stdout.flush()

            # Read stderr in real-time
            for line in process.stderr:
                line = line.rstrip('\n\r')
                stderr_buffer.append(line)
                print(line, file=sys.stderr)
                sys.stderr.flush()

            return_code = process.wait()
            duration = time.perf_counter() - start_time

            return BuildResult(
                success=return_code == 0,
                return_code=return_code,
                stdout='\n'.join(stdout_buffer),
                stderr='\n'.join(stderr_buffer),
                output_path=output_dir if return_code == 0 else None,
                duration_seconds=duration,
            )

        except subprocess.TimeoutExpired:
            duration = time.perf_counter() - start_time
            return BuildResult(
                success=False,
                return_code=-1,
                stdout='\n'.join(stdout_buffer),
                stderr="Build timed out after 1 hour",
                output_path=None,
                duration_seconds=duration,
            )
        except Exception as e:
            duration = time.perf_counter() - start_time
            return BuildResult(
                success=False,
                return_code=-2,
                stdout='\n'.join(stdout_buffer),
                stderr=f"Build error: {str(e)}",
                output_path=None,
                duration_seconds=duration,
            )

    def _run_build_capture(
        self,
        command: list[str],
        source_dir: Path,
        output_dir: Path,
        env: dict[str, str],
        start_time: float,
    ) -> BuildResult:
        """Execute build command with captured output (no real-time display).

        Args:
            command: Build command to execute
            source_dir: Source directory for the build
            output_dir: Output directory for build artifacts
            env: Environment variables
            start_time: Start time for duration calculation

        Returns:
            BuildResult with success status and output information.
        """
        try:
            result = subprocess.run(
                command,
                cwd=source_dir,
                capture_output=True,
                text=True,
                env=env,
                timeout=3600,  # 1 hour timeout
            )

            duration = time.perf_counter() - start_time

            return BuildResult(
                success=result.returncode == 0,
                return_code=result.returncode,
                stdout=result.stdout,
                stderr=result.stderr,
                output_path=output_dir if result.returncode == 0 else None,
                duration_seconds=duration,
            )

        except subprocess.TimeoutExpired:
            duration = time.perf_counter() - start_time
            return BuildResult(
                success=False,
                return_code=-1,
                stdout="",
                stderr="Build timed out after 1 hour",
                output_path=None,
                duration_seconds=duration,
            )
        except Exception as e:
            duration = time.perf_counter() - start_time
            return BuildResult(
                success=False,
                return_code=-2,
                stdout="",
                stderr=f"Build error: {str(e)}",
                output_path=None,
                duration_seconds=duration,
            )
    
    def _validate_directory(self, directory: Path, create_if_not_exists: bool = True) -> Path:
        """Validate and optionally create a directory.
        
        Args:
            directory: Directory path to validate
            create_if_not_exists: Create directory if it doesn't exist
            
        Returns:
            Resolved absolute path
            
        Raises:
            ValueError: If directory cannot be created or is not a directory
        """
        if not directory.exists():
            if create_if_not_exists:
                directory.mkdir(parents=True, exist_ok=True)
            else:
                raise ValueError(f"Directory does not exist: {directory}")
        
        if not directory.is_dir():
            raise ValueError(f"Path is not a directory: {directory}")
        
        return directory.resolve()
