"""PNPM command execution."""

import os
import subprocess
import sys
import time
from pathlib import Path
from typing import Final

from multi_lang_build.compiler.base import BuildResult


class PnpmExecutor:
    """Execute pnpm commands with streaming support."""

    DEFAULT_TIMEOUT: Final[int] = 3600  # 1 hour

    @staticmethod
    def execute(
        command: list[str],
        working_dir: Path,
        environment: dict[str, str],
        stream_output: bool = True,
    ) -> BuildResult:
        """Execute a command in the specified directory.

        Args:
            command: Command to execute
            working_dir: Working directory for the command
            environment: Environment variables
            stream_output: Whether to stream output in real-time

        Returns:
            BuildResult containing success status and output information
        """
        original_cwd = Path.cwd()
        full_command = command.copy()
        start_time: float = 0.0

        try:
            os.chdir(working_dir)
            start_time = time.perf_counter()

            if stream_output:
                return PnpmExecutor._execute_stream(
                    full_command, working_dir, environment, start_time
                )
            else:
                return PnpmExecutor._execute_capture(
                    full_command, working_dir, environment, start_time
                )

        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,
            )
        finally:
            os.chdir(original_cwd)

    @staticmethod
    def _execute_stream(
        command: list[str],
        working_dir: Path,
        environment: dict[str, str],
        start_time: float,
    ) -> BuildResult:
        """Execute with real-time output streaming."""
        stdout_buffer = []
        stderr_buffer = []

        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            env={**os.environ, **environment},
        )

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

        # Read stderr in real-time
        if process.stderr:
            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=working_dir,
            duration_seconds=duration,
        )

    @staticmethod
    def _execute_capture(
        command: list[str],
        working_dir: Path,
        environment: dict[str, str],
        start_time: float,
    ) -> BuildResult:
        """Execute with captured output."""
        result = subprocess.run(
            command,
            capture_output=True,
            text=True,
            timeout=PnpmExecutor.DEFAULT_TIMEOUT,
            env={**os.environ, **environment},
        )

        duration = time.perf_counter() - start_time

        return BuildResult(
            success=result.returncode == 0,
            return_code=result.returncode,
            stdout=result.stdout,
            stderr=result.stderr,
            output_path=working_dir,
            duration_seconds=duration,
        )
