"""
Sequential task execution with full context passing.

This module provides the core execution engine that runs tasks sequentially
while maintaining complete context between each task. Each task receives
the full conversation history and outputs from previous tasks.

Requirements: AGENT-02 (sequential execution), AGENT-06 (validation),
             AGENT-07 (progress reporting)
"""

from typing import List, Callable, Optional, Dict, Any
from dataclasses import dataclass, field
from datetime import datetime

from gsd_rlm.session.memory import SessionMessage, TaskOutput


@dataclass
class AgentMessage:
    """Message passed between agents in the execution pipeline.

    This is a simplified version that can work standalone without
    the full rlm_toolkit integration.
    """

    task_id: str
    content: Any
    history: List[Dict[str, str]] = field(default_factory=list)
    routing: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class TaskResult:
    """Result of a single task execution."""

    task: str
    content: Any
    success: bool
    error: Optional[str] = None
    routing: Optional[List[str]] = None


class MultiAgentRuntime:
    """Simplified multi-agent runtime for sequential execution.

    This is a minimal implementation that can process messages through
    a sequence of agents. It can be replaced with the full rlm_toolkit
    runtime when available.
    """

    def __init__(self, agents: Optional[List[Any]] = None):
        """Initialize the runtime with optional agents.

        Args:
            agents: List of agent instances to route messages through
        """
        self.agents = agents or []

    def run(
        self,
        message: AgentMessage,
        on_step: Optional[Callable[[AgentMessage, Any], None]] = None,
    ) -> AgentMessage:
        """Run message through the agent pipeline.

        Args:
            message: The input message to process
            on_step: Optional callback invoked after each agent step

        Returns:
            The processed message with results
        """
        result = message

        for agent in self.agents:
            if on_step:
                on_step(result, agent)

            # Process through agent if it has a process method
            if hasattr(agent, "process"):
                processed = agent.process(result)
                if processed:
                    if isinstance(processed, list) and len(processed) > 0:
                        result = processed[0]
                    else:
                        result = processed

        return result


class SequentialTaskRunner:
    """Execute tasks sequentially with full context passing (AGENT-02).

    This is the core execution engine that runs tasks in sequence while
    preserving complete context between them. Each task receives:

    1. The full conversation history from the session
    2. Outputs from all previous tasks in the current run
    3. Routing information showing which agents were used

    Example:
        ```python
        from gsd_rlm.session.memory import FileSessionMemory
        from gsd_rlm.execution.runner import SequentialTaskRunner

        session_memory = FileSessionMemory(Path("sessions"))
        runtime = MultiAgentRuntime(agents=[agent1, agent2])

        runner = SequentialTaskRunner(
            runtime=runtime,
            session_memory=session_memory,
            on_progress=lambda status, task, detail: print(f"[{status}] {task}")
        )

        result = runner.run_tasks(
            tasks=["Analyze the code", "Write tests", "Fix issues"],
            session_id="session-123",
            agent_ids=["analyzer", "coder", "fixer"]
        )
        ```
    """

    def __init__(
        self,
        runtime: MultiAgentRuntime,
        session_memory: "FileSessionMemory",
        on_progress: Optional[Callable[[str, str, str], None]] = None,
    ):
        """Initialize the sequential task runner.

        Args:
            runtime: Multi-agent runtime for executing tasks
            session_memory: Session persistence for context
            on_progress: Optional callback for progress updates
        """
        self.runtime = runtime
        self.session = session_memory
        self.on_progress = on_progress

    def run_tasks(
        self,
        tasks: List[str],
        session_id: str,
        agent_ids: List[str],
    ) -> TaskResult:
        """Execute tasks sequentially, passing context between each.

        Args:
            tasks: List of task descriptions to execute
            session_id: Session identifier for persistence
            agent_ids: List of agent IDs for routing

        Returns:
            TaskResult from the final task (or first failure)
        """
        from gsd_rlm.execution.validation import validate_input, validate_output

        # Load or create session
        session = self.session.load(session_id)
        if not session:
            session = self.session.create(
                session_id, agent_ids[0] if agent_ids else "default"
            )

        # Build initial message with session context
        message = AgentMessage(
            task_id=session_id,
            content=tasks[0] if tasks else "",
            history=self._session_to_history(session),
            routing=agent_ids,
        )

        results = []

        # Execute each task sequentially
        for i, task in enumerate(tasks):
            self._report_progress("starting", task, f"Task {i + 1}/{len(tasks)}")

            # Validate input (AGENT-06)
            try:
                validate_input(task)
            except ValueError as e:
                self._report_progress("failed", task, str(e))
                return TaskResult(
                    task=task,
                    content=None,
                    success=False,
                    error=f"Input validation failed: {e}",
                )

            message.content = task

            try:
                # Run through agent pipeline
                result = self.runtime.run(
                    message,
                    on_step=self._make_step_callback(session),
                )

                # Validate output (AGENT-06)
                validate_output(result.content)

                # Store task output in session
                from gsd_rlm.session.memory import TaskOutput as SessionTaskOutput

                task_output = SessionTaskOutput(
                    task=task,
                    result=str(result.content)[:1000],  # Truncate for storage
                    success=True,
                    routing=result.routing.copy() if result.routing else None,
                )
                session.task_outputs.append(task_output)
                self.session.save(session)

                results.append(
                    TaskResult(
                        task=task,
                        content=result.content,
                        success=True,
                        routing=result.routing.copy() if result.routing else None,
                    )
                )

                self._report_progress("completed", task, str(result.content)[:100])

                # Pass full context to next task
                message = result

            except Exception as e:
                self._report_progress("failed", task, str(e))
                from gsd_rlm.session.memory import TaskOutput as SessionTaskOutput

                task_output = SessionTaskOutput(
                    task=task,
                    result=None,
                    success=False,
                    error=str(e),
                )
                session.task_outputs.append(task_output)
                self.session.save(session)

                return TaskResult(task=task, content=None, success=False, error=str(e))

        # Return final result
        final = (
            results[-1]
            if results
            else TaskResult(
                task="", content=None, success=False, error="No tasks to execute"
            )
        )
        return final

    def _session_to_history(self, session) -> List[dict]:
        """Convert session messages to AgentMessage history format.

        Args:
            session: SessionState to convert

        Returns:
            List of {role, content} dicts
        """
        return [{"role": m.role, "content": m.content} for m in session.messages]

    def _make_step_callback(self, session):
        """Create callback that updates session on each agent step.

        Args:
            session: SessionState to update

        Returns:
            Callback function for runtime steps
        """

        def callback(message, agent):
            from gsd_rlm.session.memory import SessionMessage as SessionMsg

            agent_name = getattr(agent, "name", str(agent))
            session_message = SessionMsg(
                role="agent",
                content=f"[{agent_name}] Processing...",
                timestamp=datetime.utcnow().isoformat(),
            )
            session.messages.append(session_message)
            self.session.save(session)

        return callback

    def _report_progress(self, status: str, task: str, detail: str):
        """Report progress if callback provided (AGENT-07).

        Args:
            status: Status string (starting, completed, failed)
            task: Task description
            detail: Additional detail message
        """
        if self.on_progress:
            self.on_progress(status, task, detail)
