"""System command executor"""

import subprocess
import asyncio
from typing import AsyncIterator, Optional

from diona.project.cli.core.models import ParsedInput, ExecutionResult, StreamChunk
from diona.project.cli.executor import BaseExecutor


def decode_output(data: bytes) -> str:
    """Decode output bytes trying multiple encodings"""
    if not data:
        return ""
    encodings = ['utf-8', 'gbk', 'cp936', 'latin1']
    for encoding in encodings:
        try:
            return data.decode(encoding)
        except:
            continue
    return data.decode('utf-8', errors='replace')


class SystemExecutor(BaseExecutor):
    """Executor for system commands"""

    _instance: Optional["SystemExecutor"] = None

    def __new__(cls) -> "SystemExecutor":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
    
    def can_execute(self, parsed_input: ParsedInput) -> bool:
        return parsed_input.input_type.value == "system"
    
    def execute(self, parsed_input: ParsedInput) -> ExecutionResult:
        from diona.project.cli.core.result_storage import get_result_storage
        
        command = " ".join([parsed_input.command_name] + parsed_input.args)
        
        try:
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
            )
            stdout = decode_output(result.stdout)
            stderr = decode_output(result.stderr)
            
            # Store result
            storage = get_result_storage()
            storage.set_result(result.returncode, stdout, stderr)
            
            return ExecutionResult(
                success=(result.returncode == 0),
                return_code=result.returncode,
                stdout=stdout,
                stderr=stderr
            )
        except Exception as e:
            error_msg = str(e)
            # Store result
            storage = get_result_storage()
            storage.set_result(None, "", error_msg)
            return ExecutionResult(
                success=False,
                return_code=-1,
                error_message=error_msg
            )
    
    async def execute_stream(self, parsed_input: ParsedInput) -> AsyncIterator[StreamChunk]:
        command = " ".join([parsed_input.command_name] + parsed_input.args)
        
        process = subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        
        while True:
            stdout_bytes = process.stdout.readline()
            if stdout_bytes:
                yield StreamChunk(content=decode_output(stdout_bytes))
            
            stderr_bytes = process.stderr.readline()
            if stderr_bytes:
                yield StreamChunk(content=decode_output(stderr_bytes), is_error=True)
            
            if process.poll() is not None:
                break
            
            await asyncio.sleep(0.05)
        
        remaining_out = process.stdout.read()
        if remaining_out:
            yield StreamChunk(content=decode_output(remaining_out))
        
        remaining_err = process.stderr.read()
        if remaining_err:
            yield StreamChunk(content=decode_output(remaining_err), is_error=True)
        
        yield StreamChunk(content="", is_final=True, return_code=process.returncode)
