"""ASV benchmarks for ComponentResolver scope chain resolution.

Measures the performance of:
- Global default resolution
- Project override resolution
- Plan override resolution (3-level chain)
- Cache hit performance
- Registration throughput
- Introspection API latency
"""

from __future__ import annotations

import importlib
import sys
from pathlib import Path
from typing import Any, Protocol, runtime_checkable

# Ensure the local *source* tree is importable even when ASV has an
# older build of the package installed.
_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.component_resolver import (  # noqa: E402
    ComponentResolver,
)

# ---------------------------------------------------------------------------
# Test protocol and implementations
# ---------------------------------------------------------------------------


@runtime_checkable
class _BenchProtocol(Protocol):
    def run(self) -> str: ...


class _BenchGlobal:
    def run(self) -> str:
        return "global"


class _BenchProject:
    def run(self) -> str:
        return "project"


class _BenchPlan:
    def run(self) -> str:
        return "plan"


# ---------------------------------------------------------------------------
# Timing suite
# ---------------------------------------------------------------------------


class ComponentResolverTimeSuite:
    """Benchmark timing for component resolution across scope levels."""

    def setup(self) -> None:
        self._resolver = ComponentResolver()
        self._resolver.register_global(_BenchProtocol, _BenchGlobal())
        self._resolver.register_project("proj-1", _BenchProtocol, _BenchProject())
        self._resolver.register_plan("plan-1", _BenchProtocol, _BenchPlan())

    def time_resolve_global(self) -> None:
        """Resolve from global scope (no plan/project context)."""
        self._resolver.invalidate_cache()
        self._resolver.resolve(_BenchProtocol)

    def time_resolve_project(self) -> None:
        """Resolve from project scope."""
        self._resolver.invalidate_cache()
        self._resolver.resolve(_BenchProtocol, project_id="proj-1")

    def time_resolve_plan(self) -> None:
        """Resolve from plan scope (full 3-level chain)."""
        self._resolver.invalidate_cache()
        self._resolver.resolve(_BenchProtocol, plan_id="plan-1", project_id="proj-1")

    def time_resolve_cache_hit(self) -> None:
        """Resolve with cache hit (pre-warmed)."""
        # Prime cache
        self._resolver.resolve(_BenchProtocol)
        # Measure cache hit
        self._resolver.resolve(_BenchProtocol)

    def time_resolve_fallthrough(self) -> None:
        """Resolve with fallthrough (unknown plan/project)."""
        self._resolver.invalidate_cache()
        self._resolver.resolve(_BenchProtocol, plan_id="unknown", project_id="unknown")

    def time_register_global(self) -> None:
        """Register a global implementation."""
        resolver = ComponentResolver()
        resolver.register_global(_BenchProtocol, _BenchGlobal())

    def time_register_project(self) -> None:
        """Register a project implementation."""
        resolver = ComponentResolver()
        resolver.register_project("p", _BenchProtocol, _BenchProject())

    def time_register_plan(self) -> None:
        """Register a plan implementation."""
        resolver = ComponentResolver()
        resolver.register_plan("p", _BenchProtocol, _BenchPlan())

    def time_has_global(self) -> None:
        """Check has_global."""
        self._resolver.has_global(_BenchProtocol)

    def time_list_global_types(self) -> None:
        """List global type names."""
        self._resolver.list_global_types()


# ---------------------------------------------------------------------------
# Memory suite
# ---------------------------------------------------------------------------


class ComponentResolverMemSuite:
    """Benchmark memory consumption for component resolver operations."""

    def setup(self) -> None:
        self._resolver = ComponentResolver()
        self._resolver.register_global(_BenchProtocol, _BenchGlobal())
        for i in range(100):
            self._resolver.register_project(
                f"proj-{i}", _BenchProtocol, _BenchProject()
            )
            self._resolver.register_plan(f"plan-{i}", _BenchProtocol, _BenchPlan())

    def mem_resolve_many(self) -> list[Any]:
        """Memory for resolving 100 plan-level lookups."""
        results = []
        for i in range(100):
            results.append(
                self._resolver.resolve(
                    _BenchProtocol, plan_id=f"plan-{i}", project_id=f"proj-{i}"
                )
            )
        return results
