"""Executor models and interfaces"""

from abc import ABC, abstractmethod
from typing import Optional, AsyncIterator

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


class BaseExecutor(ABC):
    """Base executor interface"""

    _instance: Optional["BaseExecutor"] = None

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

    def __init__(self) -> None:
        if self._initialized:
            return
        self._initialized = True

    @abstractmethod
    def can_execute(self, parsed_input: ParsedInput) -> bool:
        """Check if this executor can handle the input"""
        pass

    @abstractmethod
    def execute(self, parsed_input: ParsedInput) -> ExecutionResult:
        """Execute the command synchronously"""
        pass

    @abstractmethod
    async def execute_stream(self, parsed_input: ParsedInput) -> AsyncIterator[StreamChunk]:
        """Execute the command with streaming output"""
        pass


class ExecutorRegistry:
    """Registry for executors"""

    _instance: Optional["ExecutorRegistry"] = None

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

    def __init__(self) -> None:
        if self._initialized:
            return
        self._executors: list[BaseExecutor] = []
        self._initialized = True

    def register(self, executor: BaseExecutor) -> None:
        """Register an executor"""
        for e in self._executors:
            if type(e) == type(executor):
                return
        self._executors.append(executor)

    def get_executor(self, parsed_input: ParsedInput) -> Optional[BaseExecutor]:
        """Get an executor that can handle the input"""
        for executor in self._executors:
            if executor.can_execute(parsed_input):
                return executor
        return None

    def execute(self, parsed_input: ParsedInput) -> ExecutionResult:
        """Execute using the appropriate executor"""
        executor = self.get_executor(parsed_input)
        if executor:
            return executor.execute(parsed_input)
        return ExecutionResult(
            success=False,
            return_code=-1,
            error_message=f"No executor found for input type: {parsed_input.input_type}"
        )

    async def execute_stream(self, parsed_input: ParsedInput) -> AsyncIterator[StreamChunk]:
        """Execute with streaming using the appropriate executor"""
        executor = self.get_executor(parsed_input)
        if executor:
            async for chunk in executor.execute_stream(parsed_input):
                yield chunk
        else:
            yield StreamChunk(
                content=f"No executor found for input type: {parsed_input.input_type}",
                is_error=True,
                is_final=True,
                return_code=-1
            )


def get_executor_registry() -> ExecutorRegistry:
    """Get the global executor registry"""
    return ExecutorRegistry()
