"""CLI Facade - provides access to CLI core functionality for other modules"""

import asyncio
from typing import AsyncIterator, Optional
from diona.project.cli.engine import get_engine, Engine
from diona.project.cli.engine.adapter import get_client_adapter, get_agent_adapter, get_web_adapter
from diona.project.cli.engine.adapter.client import ClientAdapter
from diona.project.cli.engine.adapter.agent import AgentAdapter
from diona.project.cli.engine.adapter.web import WebAdapter
from diona.project.cli.engine.result import StreamChunk


class CLIFacade:
    """Facade for accessing CLI core functionality from other modules"""
    
    def __init__(self):
        self._engine: Optional[Engine] = None
        self._client_adapter: Optional[ClientAdapter] = None
        self._agent_adapter: Optional[AgentAdapter] = None
        self._web_adapter: Optional[WebAdapter] = None
    
    def _get_engine(self) -> Engine:
        if self._engine is None:
            self._engine = get_engine()
        return self._engine
    
    def _get_client_adapter(self) -> ClientAdapter:
        if self._client_adapter is None:
            self._client_adapter = get_client_adapter()
        return self._client_adapter
    
    def _get_agent_adapter(self) -> AgentAdapter:
        if self._agent_adapter is None:
            self._agent_adapter = get_agent_adapter()
        return self._agent_adapter
    
    def _get_web_adapter(self) -> WebAdapter:
        if self._web_adapter is None:
            self._web_adapter = get_web_adapter()
        return self._web_adapter
    
    async def execute(self, user_input: str) -> AsyncIterator[StreamChunk]:
        """Execute user input through the main engine
        
        Args:
            user_input: The user input string to execute
            
        Yields:
            StreamChunk: Results from execution
        """
        engine = self._get_engine()
        async for chunk in engine.execute(user_input):
            yield chunk
    
    def execute_sync(self, user_input: str):
        """Execute user input through the main engine (synchronous)
        
        Args:
            user_input: The user input string to execute
        """
        engine = self._get_engine()
        engine.execute_sync(user_input)
    
    async def execute_and_wait(self, user_input: str) -> str:
        """Execute user input and wait for complete result (async)
        
        Args:
            user_input: The user input string to execute
            
        Returns:
            str: Combined result from all chunks
        """
        result_parts = []
        async for chunk in self.execute(user_input):
            result_parts.append(chunk.content)
        return "".join(result_parts)
    
    async def execute_client(self, user_input: str) -> AsyncIterator[StreamChunk]:
        """Execute user input through client adapter
        
        Client adapter supports:
        - Builtin commands (/help, /exit, etc.)
        - Registered commands
        - System commands
        
        Args:
            user_input: The user input string to execute
            
        Yields:
            StreamChunk: Results from execution
        """
        adapter = self._get_client_adapter()
        async for chunk in adapter.execute(user_input):
            yield chunk
    
    def execute_client_sync(self, user_input: str):
        """Execute user input through client adapter (synchronous)
        
        Args:
            user_input: The user input string to execute
        """
        adapter = self._get_client_adapter()
        
        async def run():
            async for chunk in adapter.execute(user_input):
                if chunk.content:
                    print(chunk.content, end="", flush=True)
                if chunk.is_final:
                    print()
        
        try:
            loop = asyncio.get_running_loop()
            asyncio.create_task(run())
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                loop.run_until_complete(run())
            finally:
                loop.close()
    
    async def execute_client_and_wait(self, user_input: str) -> str:
        """Execute client command and wait for complete result (async)
        
        Args:
            user_input: The user input string to execute
            
        Returns:
            str: Combined result from all chunks
        """
        result_parts = []
        async for chunk in self.execute_client(user_input):
            result_parts.append(chunk.content)
        return "".join(result_parts)
    
    async def execute_agent(self, user_input: str) -> AsyncIterator[StreamChunk]:
        """Execute user input through agent adapter
        
        Agent adapter supports:
        - Builtin commands
        - Agent dialogue (non-command input sent to AI)
        
        Args:
            user_input: The user input string to execute
            
        Yields:
            StreamChunk: Results from execution
        """
        adapter = self._get_agent_adapter()
        async for chunk in adapter.execute(user_input):
            yield chunk
    
    def execute_agent_sync(self, user_input: str):
        """Execute user input through agent adapter (synchronous)
        
        Args:
            user_input: The user input string to execute
        """
        adapter = self._get_agent_adapter()
        
        async def run():
            async for chunk in adapter.execute(user_input):
                if chunk.content:
                    print(chunk.content, end="", flush=True)
                if chunk.is_final:
                    print()
        
        try:
            loop = asyncio.get_running_loop()
            asyncio.create_task(run())
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                loop.run_until_complete(run())
            finally:
                loop.close()
    
    async def execute_agent_and_wait(self, user_input: str) -> str:
        """Execute agent command and wait for complete result (async)
        
        Args:
            user_input: The user input string to execute
            
        Returns:
            str: Combined result from all chunks
        """
        result_parts = []
        async for chunk in self.execute_agent(user_input):
            result_parts.append(chunk.content)
        return "".join(result_parts)
    
    async def execute_web(self, user_input: str) -> AsyncIterator[StreamChunk]:
        """Execute user input through web adapter
        
        Web adapter sends commands to remote web server for execution.
        
        Args:
            user_input: The user input string to execute
            
        Yields:
            StreamChunk: Results from remote execution
        """
        adapter = self._get_web_adapter()
        async for chunk in adapter.execute(user_input):
            yield chunk
    
    def execute_web_sync(self, user_input: str):
        """Execute user input through web adapter (synchronous)
        
        Args:
            user_input: The user input string to execute
        """
        adapter = self._get_web_adapter()
        
        async def run():
            async for chunk in adapter.execute(user_input):
                if chunk.content:
                    print(chunk.content, end="", flush=True)
                if chunk.is_final:
                    print()
        
        try:
            loop = asyncio.get_running_loop()
            asyncio.create_task(run())
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                loop.run_until_complete(run())
            finally:
                loop.close()
    
    async def execute_web_and_wait(self, user_input: str) -> str:
        """Execute web command and wait for complete result (async)
        
        Args:
            user_input: The user input string to execute
            
        Returns:
            str: Combined result from all chunks
        """
        result_parts = []
        async for chunk in self.execute_web(user_input):
            result_parts.append(chunk.content)
        return "".join(result_parts)
    
    def set_mode(self, mode: str) -> bool:
        """Set the current CLI mode
        
        Args:
            mode: One of 'client', 'agent', 'web'
            
        Returns:
            bool: True if mode was set successfully
        """
        engine = self._get_engine()
        return engine.set_mode(mode)
    
    def get_current_mode(self) -> str:
        """Get the current CLI mode
        
        Returns:
            str: Current mode ('client', 'agent', 'web')
        """
        engine = self._get_engine()
        return engine.get_current_mode()
    
    def get_prompt_prefix(self) -> str:
        """Get the prompt prefix for current mode
        
        Returns:
            str: Prompt prefix (e.g., '<diona@local>', '<agent:diona>')
        """
        engine = self._get_engine()
        return engine.get_prompt_prefix()


_facade: Optional[CLIFacade] = None


def get_cli_facade() -> CLIFacade:
    """Get the global CLI facade instance
    
    Returns:
        CLIFacade: The CLI facade singleton
    """
    global _facade
    if _facade is None:
        _facade = CLIFacade()
    return _facade
