"""Direct strategy for Manager orchestration.

The default strategy. Extracts the current Manager behavior into a strategy
class: uses zero-cost keyword overlap routing to pick the best agent
and delegates the entire task in a single shot.
"""

from typing import Generator, List, Optional, Union, Any

from orionagent.agents.base_agent import Agent
from orionagent.agents.strategies.base import BaseStrategy
from orionagent.models.base_provider import ModelProvider
from orionagent.agents.hitl import is_risky_action


class DirectStrategy(BaseStrategy):
    """Delegates the entire task to the single best agent."""

    def execute(
        self,
        task: str,
        agents: List[Agent],
        model: Optional[ModelProvider],
        system_instruction: Optional[str] = None,
        context: Optional[str] = None,
        temperature: Optional[float] = None,
        tools: Optional[List[Any]] = None,
        stream: bool = True,
        async_mode: bool = True,
        verbose: bool = False,
        debug: bool = False,
        record_trace: bool = True,
        hitl: bool = False,
        priority: Optional[str] = None,
        manager_context: Optional[str] = None,
        on_step_complete: Optional[Any] = None,
    ) -> Union[str, Generator[str, None, None], Any]:

        if context:
            prompt = (
                f"{context}\n\n"
                f"==== ACTIVE TASK ====\n"
                f"IMPORTANT: The history provided above is for context ONLY. "
                f"Your CURRENT GOAL is to address the following request:\n\n"
                f"{task}"
            )
        else:
            prompt = task
        
        # Inject global memory context from Manager into the agent prompt
        if manager_context:
            prompt = f"### GLOBAL CONTEXT ###\n{manager_context}\n\n{prompt}"

        # If there's only one agent, skip keyword overhead entirely
        if len(agents) == 1:
            selected = agents[0]
        else:
            selected = self.select_agent(task, agents)
        
        if selected is None:
            # No agent matched keywords (e.g. "hi", "what is your name?")
            # Handled by the Manager level model
            if model:
                if hitl:
                    from orionagent.agents.hitl import HitlConfig
                    h_cfg = hitl if isinstance(hitl, HitlConfig) else HitlConfig()
                    self._approve_direct(task, "Manager (Internal)", h_cfg, model=model)
                if stream:
                    return self._stream_manager(model, prompt, system_instruction, temperature, tools)
                return model.generate(
                    prompt=prompt,
                    system_instruction=system_instruction,
                    temperature=temperature,
                    tools=tools
                )
            else:
                # No model and no agent match? Fallback to first agent as a safety
                selected = agents[0]

        if hitl:
            from orionagent.agents.hitl import HitlConfig
            h_cfg = hitl if isinstance(hitl, HitlConfig) else HitlConfig()
            if not self._approve_direct(task, selected.name, h_cfg, model=model):
                raise InterruptedError("Delegation rejected by user via HITL.")

        if stream:
            def _stream_and_record():
                full_res = []
                for chunk in self._stream_response(selected, prompt, model, record_trace=record_trace, priority=priority, temperature=temperature):
                    full_res.append(chunk)
                    yield chunk
                # Record result into Manager's global memory
                if on_step_complete:
                    on_step_complete(selected.name, task, "".join(full_res))
            return _stream_and_record()
        
        result = selected.ask(
            task=prompt, 
            stream=False, 
            use_strategy=False, 
            record_memory=False, 
            record_trace=record_trace, 
            priority=priority, 
            temperature=temperature
        )
        # Record result into Manager's global memory
        if on_step_complete:
            on_step_complete(selected.name, task, result)
        return result

    def _approve_direct(self, task: str, agent_name: str, h_cfg: "HitlConfig", model: Optional[ModelProvider] = None) -> bool:
        """Interactive terminal approval for single delegation."""
        if h_cfg.permission_level == "high":
            return True
            
        if h_cfg.ask_once and h_cfg.is_session_authorized:
            return True
            
        if h_cfg.permission_level == "medium":
            # Efficient: Pass the model for LLM-based risk check
            if not is_risky_action(task, model=model if h_cfg.use_llm else None):
                return True

        print(f"\n\033[1m[ORION HITL] Delegation Approval Required\033[0m")
        print(f"Goal: {task}")
        print(f"Target Agent: {agent_name}")
        print("-" * 40)
        choice = input("Approve delegation? (y/n): ").strip().lower()
        if choice == 'y':
            print("\033[32m[APPROVED] Executing...\033[0m")
            if h_cfg.ask_once:
                h_cfg.authorize_session()
            return True
        else:
            print("\033[31m[ABORTED] Delegation rejected by user.\033[0m")
            return False

        print("\033[32m[APPROVED] Executing...\033[0m\n")


    @staticmethod
    def _stream_manager(
        model: ModelProvider, prompt: str, system_instruction: str, temperature: float, tools: list
    ) -> Generator[str, None, None]:
        yield from model.generate_stream(
            prompt=prompt,
            system_instruction=system_instruction,
            temperature=temperature,
            tools=tools
        )

    def _stream_response(
        self, agent: Agent, task: str, model: Any = None, record_trace: bool = True, priority: Optional[str] = None, temperature: float = None
    ) -> Generator[str, None, None]:
        yield from agent.ask(
            task=task, 
            stream=True, 
            use_strategy=False, 
            record_memory=False, 
            record_trace=record_trace, 
            priority=priority, 
            temperature=temperature
        )
