"""ASV benchmarks for ACMS Pipeline Orchestrator and Phase 1 components.

Measures the performance of:
- ConfidenceWeightedSelector strategy selection
- ProportionalBudgetAllocator budget distribution
- CircuitBreaker failure tracking
- ParallelStrategyExecutor single and multi-strategy execution
- ContextAssemblyPipeline full assembly with timing
"""

from __future__ import annotations

import importlib
import sys
from collections.abc import Sequence
from pathlib import Path
from typing import Any

_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

import cleveragents  # noqa: E402

importlib.reload(cleveragents)

from cleveragents.application.services.acms_pipeline import (  # noqa: E402
    CircuitBreaker,
    ConfidenceWeightedSelector,
    ContextAssemblyPipeline,
    ParallelStrategyExecutor,
    ProportionalBudgetAllocator,
)
from cleveragents.application.services.acms_service import (  # noqa: E402
    RecencyStrategy,
    RelevanceStrategy,
    StrategyCapabilities,
    TieredStrategy,
)
from cleveragents.domain.models.core.context_fragment import (  # noqa: E402
    ContextBudget,
    ContextFragment,
    FragmentProvenance,
)

_DEFAULT_PROV = FragmentProvenance(resource_uri="bench://orchestrator")


def _make_frag(**kwargs: Any) -> ContextFragment:
    kwargs.setdefault("uko_node", "bench://orchestrator")
    kwargs.setdefault("token_count", 10)
    kwargs.setdefault("provenance", _DEFAULT_PROV)
    return ContextFragment(**kwargs)


class _BenchStrategy:
    """Minimal strategy for benchmarks."""

    def __init__(self, name: str = "bench") -> None:
        self._name = name

    @property
    def name(self) -> str:
        return self._name

    @property
    def capabilities(self) -> StrategyCapabilities:
        return StrategyCapabilities()

    def can_handle(self, request: dict[str, Any]) -> float:
        return 0.7

    def assemble(
        self,
        fragments: Sequence[ContextFragment],
        budget: ContextBudget,
    ) -> Sequence[ContextFragment]:
        result: list[ContextFragment] = []
        total = 0
        for frag in fragments:
            if total + frag.token_count <= budget.available_tokens:
                result.append(frag)
                total += frag.token_count
        return result

    def explain(self) -> str:
        return "Benchmark strategy."


class ConfidenceWeightedSelectorSuite:
    """Benchmark ConfidenceWeightedSelector throughput."""

    def setup(self) -> None:
        self.selector = ConfidenceWeightedSelector()
        self.strategies = [
            RelevanceStrategy(),
            RecencyStrategy(),
            TieredStrategy(),
        ]

    def time_select_3_strategies(self) -> None:
        """Select from 3 built-in strategies."""
        self.selector.select(self.strategies, {})

    def time_select_with_preferred(self) -> None:
        """Select with preferred_strategies boost."""
        self.selector.select(self.strategies, {"preferred_strategies": ["recency"]})


class ProportionalBudgetAllocatorSuite:
    """Benchmark ProportionalBudgetAllocator throughput."""

    def setup(self) -> None:
        self.allocator = ProportionalBudgetAllocator()
        self.candidates_2 = [
            (RelevanceStrategy(), 0.8),
            (RecencyStrategy(), 0.2),
        ]
        self.candidates_3 = [
            (RelevanceStrategy(), 0.5),
            (RecencyStrategy(), 0.3),
            (TieredStrategy(), 0.2),
        ]

    def time_allocate_2_candidates(self) -> None:
        """Allocate budget across 2 candidates."""
        self.allocator.allocate(self.candidates_2, 1000)

    def time_allocate_3_candidates(self) -> None:
        """Allocate budget across 3 candidates."""
        self.allocator.allocate(self.candidates_3, 1000)


class CircuitBreakerSuite:
    """Benchmark CircuitBreaker operations."""

    def time_record_success(self) -> None:
        """Record a success."""
        cb = CircuitBreaker()
        cb.record_success("test")

    def time_record_failure(self) -> None:
        """Record a failure."""
        cb = CircuitBreaker()
        cb.record_failure("test")

    def time_is_open_check(self) -> None:
        """Check if circuit is open."""
        cb = CircuitBreaker()
        cb.is_open("test")


class ParallelStrategyExecutorSuite:
    """Benchmark ParallelStrategyExecutor throughput."""

    def setup(self) -> None:
        self.executor = ParallelStrategyExecutor()
        self.fragments = [
            _make_frag(
                uko_node=f"bench://file{i}.py",
                content=f"content_{i}",
                token_count=10,
                relevance_score=0.5,
            )
            for i in range(20)
        ]
        self.budget = ContextBudget(max_tokens=500, reserved_tokens=0)
        self.strategy = _BenchStrategy()

    def time_execute_single_strategy(self) -> None:
        """Execute a single strategy (synchronous path)."""
        self.executor.execute(
            [(self.strategy, 0.7, 200)],
            self.fragments,
            self.budget,
        )


class ContextAssemblyPipelineSuite:
    """Benchmark full pipeline assembly."""

    def setup(self) -> None:
        self.pipeline = ContextAssemblyPipeline()
        self.budget = ContextBudget(max_tokens=500, reserved_tokens=0)
        self.fragments_small = [
            _make_frag(
                uko_node=f"bench://s{i}.py",
                content=f"small_{i}",
                token_count=10,
                relevance_score=round(0.9 - i * 0.05, 2),
            )
            for i in range(5)
        ]
        self.fragments_medium = [
            _make_frag(
                uko_node=f"bench://m{i}.py",
                content=f"medium_{i}",
                token_count=10,
                relevance_score=round(0.9 - i * 0.01, 2),
            )
            for i in range(50)
        ]

    def time_assemble_5_fragments(self) -> None:
        """Assemble 5 fragments via full pipeline."""
        self.pipeline.assemble(
            plan_id="01JQTESTPN00000000000000AA",
            fragments=self.fragments_small,
            budget=self.budget,
            strategy="relevance",
        )

    def time_assemble_50_fragments(self) -> None:
        """Assemble 50 fragments via full pipeline."""
        self.pipeline.assemble(
            plan_id="01JQTESTPN00000000000000AA",
            fragments=self.fragments_medium,
            budget=self.budget,
            strategy="relevance",
        )
