"""Registered command executor"""

from typing import AsyncIterator, Optional

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


class RegisteredExecutor(BaseExecutor):
    """Executor for registered commands"""

    _instance: Optional["RegisteredExecutor"] = None

    def __new__(cls) -> "RegisteredExecutor":
        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._command_manager = self._get_command_manager()
        self._initialized = True
    
    def _get_command_manager(self):
        from diona.project.command import CommandManager
        return CommandManager()
    
    def can_execute(self, parsed_input: ParsedInput) -> bool:
        return parsed_input.input_type.value == "registered"
    
    def execute(self, parsed_input: ParsedInput) -> ExecutionResult:
        from io import StringIO
        import sys
        from diona.project.cli.core.result_storage import get_result_storage
        
        stdout_capture = StringIO()
        stderr_capture = StringIO()
        
        old_stdout = sys.stdout
        old_stderr = sys.stderr
        
        try:
            sys.stdout = stdout_capture
            sys.stderr = stderr_capture
            
            if self._command_manager.has_command(parsed_input.command_name):
                cmd = self._command_manager.get_command(parsed_input.command_name)
                return_value = cmd.implementation(parsed_input.args)
                
                sys.stdout = old_stdout
                sys.stderr = old_stderr
                
                stdout_value = stdout_capture.getvalue()
                stderr_value = stderr_capture.getvalue()
                
                # Store result
                storage = get_result_storage()
                storage.set_result(return_value, stdout_value, stderr_value)
                
                return ExecutionResult(
                    success=True,
                    return_code=0,
                    stdout=stdout_value,
                    stderr=stderr_value
                )
            else:
                sys.stdout = old_stdout
                sys.stderr = old_stderr
                
                error_msg = f"Command not found: {parsed_input.command_name}"
                # Store result
                storage = get_result_storage()
                storage.set_result(None, "", error_msg)
                return ExecutionResult(
                    success=False,
                    return_code=-1,
                    stderr=error_msg
                )
        except Exception as e:
            sys.stdout = old_stdout
            sys.stderr = old_stderr
            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]:
        result = self.execute(parsed_input)
        if result.success:
            if result.stdout:
                for line in result.stdout.splitlines(keepends=True):
                    yield StreamChunk(content=line)
        else:
            yield StreamChunk(
                content=result.error_message or "Unknown error",
                is_error=True,
                is_final=True,
                return_code=result.return_code
            )
        
        yield StreamChunk(content="", is_final=True, return_code=result.return_code)
