"""Main orchestrator for deliberate workflow."""

import time
from pathlib import Path

from deliberate.adapters.base import ModelAdapter
from deliberate.adapters.cli_adapter import CLIAdapter
from deliberate.adapters.fake_adapter import FakeAdapter
from deliberate.adapters.mcp_adapter import MCPAdapter
from deliberate.budget.tracker import BudgetTracker
from deliberate.config import DeliberateConfig
from deliberate.git.worktree import WorktreeManager
from deliberate.phases.execution import ExecutionPhase
from deliberate.phases.planning import PlanningPhase
from deliberate.phases.review import ReviewPhase
from deliberate.tracing.setup import get_tracer
from deliberate.types import JuryResult


class Orchestrator:
    """Orchestrates the complete deliberate workflow.

    Coordinates the three phases (planning, execution, review)
    and manages adapters, worktrees, and budget.
    """

    def __init__(
        self,
        config: DeliberateConfig,
        repo_root: Path,
    ):
        """Initialize the orchestrator.

        Args:
            config: The deliberate configuration.
            repo_root: Path to the repository root.
        """
        self.config = config
        self.repo_root = repo_root.resolve()

        # Initialize budget tracker
        self.budget = BudgetTracker(
            max_total_tokens=config.limits.budget.max_total_tokens,
            max_cost_usd=config.limits.budget.max_cost_usd,
            max_requests_per_agent=config.limits.budget.max_requests_per_agent,
            hard_timeout_seconds=config.limits.time.hard_timeout_minutes * 60,
        )

        # Initialize worktree manager
        worktree_root = repo_root / config.workflow.execution.worktree.root
        self.worktrees = WorktreeManager(repo_root, worktree_root)

        # Build adapters
        self.adapters = self._build_adapters()

        # Initialize phases
        self.planning = self._build_planning_phase()
        self.execution = self._build_execution_phase()
        self.review = self._build_review_phase()

    def _build_adapters(self) -> dict[str, ModelAdapter]:
        """Build adapters for all configured agents."""
        adapters: dict[str, ModelAdapter] = {}

        for name, agent_cfg in self.config.agents.items():
            adapter: ModelAdapter

            if agent_cfg.type == "fake":
                adapter = FakeAdapter(
                    name=name,
                    behavior=agent_cfg.behavior or "echo",
                )
            elif agent_cfg.type == "cli":
                adapter = CLIAdapter(
                    name=name,
                    command=agent_cfg.command,
                    env=agent_cfg.env,
                    timeout_seconds=agent_cfg.config.timeout_seconds,
                )
            elif agent_cfg.type == "mcp":
                # Use MCP adapter for JSON-RPC communication
                adapter = MCPAdapter(
                    name=name,
                    command=agent_cfg.command,
                    env=agent_cfg.env,
                    timeout_seconds=agent_cfg.config.timeout_seconds,
                )
            elif agent_cfg.type == "api":
                # API adapter would be implemented for direct API calls
                # For now, fall back to fake adapter
                adapter = FakeAdapter(
                    name=name,
                    behavior="critic",
                )
            else:
                # Unknown type, use fake
                adapter = FakeAdapter(name=name, behavior="echo")

            adapters[name] = adapter

        return adapters

    def _build_planning_phase(self) -> PlanningPhase:
        """Build the planning phase."""
        planning_cfg = self.config.workflow.planning
        return PlanningPhase(
            agents=planning_cfg.agents,
            adapters=self.adapters,
            budget=self.budget,
            debate_enabled=planning_cfg.debate.enabled,
            debate_rounds=planning_cfg.debate.rounds,
            selection_method=planning_cfg.selection.method,
            judge_agent=planning_cfg.selection.judge,
        )

    def _build_execution_phase(self) -> ExecutionPhase:
        """Build the execution phase."""
        execution_cfg = self.config.workflow.execution
        return ExecutionPhase(
            agents=execution_cfg.agents,
            adapters=self.adapters,
            budget=self.budget,
            worktree_mgr=self.worktrees,
            use_worktrees=execution_cfg.worktree.enabled,
            timeout_seconds=self.config.limits.time.phase_timeouts.get("execution", 25) * 60,
        )

    def _build_review_phase(self) -> ReviewPhase:
        """Build the review phase."""
        review_cfg = self.config.workflow.review
        return ReviewPhase(
            agents=review_cfg.agents,
            adapters=self.adapters,
            budget=self.budget,
            criteria=review_cfg.scoring.criteria,
            scale=review_cfg.scoring.scale,
            aggregation_method=review_cfg.aggregation.method,
            approval_threshold=review_cfg.aggregation.min_approval_ratio,
        )

    async def run(self, task: str) -> JuryResult:
        """Run the complete jury workflow.

        Args:
            task: The task description.

        Returns:
            JuryResult containing the outcome of all phases.
        """
        tracer = get_tracer()
        start_time = time.time()
        planning_cfg = self.config.workflow.planning
        execution_cfg = self.config.workflow.execution
        review_cfg = self.config.workflow.review

        with tracer.start_as_current_span("workflow.jury") as workflow_span:
            workflow_span.set_attribute("workflow.task", task[:500])
            workflow_span.set_attribute("workflow.planning_enabled", planning_cfg.enabled)
            workflow_span.set_attribute("workflow.execution_enabled", execution_cfg.enabled)
            workflow_span.set_attribute("workflow.review_enabled", review_cfg.enabled)

            try:
                # Phase 1: Planning
                selected_plan = None
                if planning_cfg.enabled and planning_cfg.agents:
                    with tracer.start_as_current_span("phase.planning") as span:
                        span.set_attribute("phase.agents", ",".join(planning_cfg.agents))
                        selected_plan = await self.planning.run(task)
                        if selected_plan:
                            span.set_attribute("phase.plan_agent", selected_plan.agent)

                # Phase 2: Execution
                execution_results = []
                if execution_cfg.enabled and execution_cfg.agents:
                    with tracer.start_as_current_span("phase.execution") as span:
                        span.set_attribute("phase.agents", ",".join(execution_cfg.agents))
                        execution_results = await self.execution.run(task, selected_plan)
                        span.set_attribute("phase.result_count", len(execution_results))
                        successful = sum(1 for e in execution_results if e.success)
                        span.set_attribute("phase.successful_count", successful)

                # Phase 3: Review
                reviews = []
                vote_result = None
                if review_cfg.enabled and review_cfg.agents and execution_results:
                    with tracer.start_as_current_span("phase.review") as span:
                        span.set_attribute("phase.agents", ",".join(review_cfg.agents))
                        reviews, vote_result = await self.review.run(task, execution_results)
                        span.set_attribute("phase.review_count", len(reviews))
                        if vote_result:
                            span.set_attribute("phase.winner_id", vote_result.winner_id)
                            span.set_attribute("phase.confidence", vote_result.confidence)

                # Phase 4: Refinement (Iterative Repair)
                refinement_iterations = []
                refinement_triggered = False
                final_improvement = 0.0

                if self.config.workflow.refinement.enabled and vote_result and reviews:
                    from deliberate.phases.refinement import RefinementOrchestrator

                    with tracer.start_as_current_span("phase.refinement") as span:
                        refinement_orch = RefinementOrchestrator(
                            config=self.config.workflow.refinement,
                            agents=list(self.adapters.values()),
                            budget_tracker=self.budget,
                            worktree_mgr=self.worktrees,
                        )

                        should_refine = await refinement_orch.should_trigger(
                            vote_result, reviews
                        )
                        span.set_attribute("phase.triggered", should_refine)

                        if should_refine:
                            refinement_triggered = True
                            refinement_iterations = await refinement_orch.run_refinement_loop(
                                initial_results=execution_results,
                                initial_reviews=reviews,
                                initial_vote=vote_result,
                                task_description=task,
                            )

                            span.set_attribute("phase.iteration_count", len(refinement_iterations))

                            # Use final refined results
                            if refinement_iterations:
                                final_iteration = refinement_iterations[-1]
                                vote_result = final_iteration.vote_result
                                reviews = final_iteration.reviews
                                final_improvement = sum(
                                    it.improvement_delta for it in refinement_iterations
                                )
                                span.set_attribute("phase.final_improvement", final_improvement)

                # Determine final diff
                final_diff = None
                if vote_result and execution_results:
                    winner = next(
                        (e for e in execution_results if e.id == vote_result.winner_id),
                        None,
                    )
                    if winner:
                        final_diff = winner.diff
                elif execution_results:
                    # No review, use first successful result
                    successful = [e for e in execution_results if e.success]
                    if successful:
                        final_diff = successful[0].diff

                # Build summary
                summary = self._build_summary(
                    task,
                    selected_plan,
                    execution_results,
                    vote_result,
                )

                # Calculate totals
                totals = self.budget.get_totals()

                result = JuryResult(
                    task=task,
                    selected_plan=selected_plan,
                    execution_results=execution_results,
                    reviews=reviews,
                    vote_result=vote_result,
                    final_diff=final_diff,
                    summary=summary,
                    success=True,
                    total_duration_seconds=time.time() - start_time,
                    total_token_usage=totals["tokens"],
                    total_cost_usd=totals["cost_usd"],
                    refinement_iterations=refinement_iterations,
                    refinement_triggered=refinement_triggered,
                    final_improvement=final_improvement,
                )

                # Record final metrics
                workflow_span.set_attribute("workflow.success", True)
                workflow_span.set_attribute("workflow.total_tokens", totals["tokens"])
                workflow_span.set_attribute("workflow.total_cost_usd", totals["cost_usd"])
                workflow_span.set_attribute(
                    "workflow.duration_seconds", result.total_duration_seconds
                )

                return result

            except Exception as e:
                totals = self.budget.get_totals()
                workflow_span.set_attribute("workflow.success", False)
                workflow_span.set_attribute("workflow.error", str(e))
                workflow_span.record_exception(e)

                return JuryResult(
                    task=task,
                    selected_plan=None,
                    execution_results=[],
                    reviews=[],
                    vote_result=None,
                    final_diff=None,
                    summary=f"Jury failed: {str(e)}",
                    success=False,
                    error=str(e),
                    total_duration_seconds=time.time() - start_time,
                    total_token_usage=totals["tokens"],
                    total_cost_usd=totals["cost_usd"],
                )

            finally:
                # Cleanup worktrees if configured
                if self.config.workflow.execution.worktree.cleanup:
                    self.worktrees.cleanup_all()

    def _build_summary(
        self,
        task: str,
        plan,
        execution_results,
        vote_result,
    ) -> str:
        """Build a summary of the jury run."""
        lines = []

        lines.append(f"## Task\n{task[:200]}...")

        if plan:
            lines.append(f"\n## Selected Plan (by {plan.agent})\n{plan.content[:500]}...")

        if execution_results:
            successful = [e for e in execution_results if e.success]
            lines.append(f"\n## Execution\n- Total agents: {len(execution_results)}")
            lines.append(f"- Successful: {len(successful)}")

        if vote_result:
            lines.append("\n## Review Results")
            lines.append(f"- Winner: {vote_result.winner_id}")
            lines.append(f"- Confidence: {vote_result.confidence:.2f}")
            lines.append("- Rankings: " + " > ".join(vote_result.rankings[:3]))

        return "\n".join(lines)
