"""ASV benchmarks for multi-project subplan overhead.

Measures the time to initialize scopes, resolve aliases, record
changesets, and validate cross-project constraints at various
project counts.  The benchmark covers the hot paths that run
during multi-project plan orchestration.
"""

from __future__ import annotations

import importlib
import sys
from pathlib import Path
from unittest.mock import MagicMock

# 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.multi_project_service import (  # noqa: E402
    MultiProjectService,
)
from cleveragents.domain.models.core.multi_project import (  # noqa: E402
    ChangeSetSummary,
    ProjectScopeResolver,
)
from cleveragents.domain.models.core.plan import (  # noqa: E402
    NamespacedName,
    Plan,
    PlanIdentity,
    PlanPhase,
    ProcessingState,
    ProjectLink,
)

_PLAN_ID = "01HXAAAAAAAAAAAAAAAAAAAAAA"


def _mock_decision_service() -> MagicMock:
    mock = MagicMock()
    mock.list_by_type.return_value = []
    return mock


def _build_plan(count: int) -> tuple[Plan, dict[str, list[str]]]:
    """Generate a plan with *count* project links and matching resources."""
    links = [ProjectLink(project_name=f"proj-{i:04d}") for i in range(count)]
    resources = {
        f"proj-{i:04d}": [f"res-{i}-{j}" for j in range(5)] for i in range(count)
    }
    plan = Plan(
        identity=PlanIdentity(plan_id=_PLAN_ID),
        namespaced_name=NamespacedName.parse("local/bench-mp"),
        description="Benchmark multi-project plan",
        action_name="local/bench-action",
        phase=PlanPhase.STRATEGIZE,
        processing_state=ProcessingState.QUEUED,
        project_links=links,
    )
    return plan, resources


class MultiProjectSmallSuite:
    """Benchmark multi-project operations with 3 projects."""

    def setup(self) -> None:
        self._svc = MultiProjectService(decision_service=_mock_decision_service())
        self._resolver = ProjectScopeResolver()
        self._plan, self._resources = _build_plan(3)
        self._initialized = self._svc.initialize_scopes(self._plan, self._resources)

    def time_initialize_scopes(self) -> None:
        """Initialize scopes for 3 projects."""
        self._svc.initialize_scopes(self._plan, self._resources)

    def time_resolve_alias_by_name(self) -> None:
        """Resolve a project by name."""
        self._resolver.resolve_alias(self._plan.project_links, "proj-0001")

    def time_validate_access_modes(self) -> None:
        """Validate access modes for 3 projects."""
        self._resolver.validate_access_modes(
            self._plan.project_links, allow_mixed=False
        )

    def time_record_changeset(self) -> None:
        """Record a changeset summary."""
        summary = ChangeSetSummary(
            project_name="proj-0000",
            files_changed=5,
            total_lines_changed=100,
        )
        self._svc.record_changeset(self._initialized, "proj-0000", summary)

    def time_validate_cross_project(self) -> None:
        """Validate cross-project constraints."""
        self._svc.validate_cross_project(self._initialized)


class MultiProjectMediumSuite:
    """Benchmark multi-project operations with 20 projects."""

    def setup(self) -> None:
        self._svc = MultiProjectService(decision_service=_mock_decision_service())
        self._resolver = ProjectScopeResolver()
        self._plan, self._resources = _build_plan(20)
        self._initialized = self._svc.initialize_scopes(self._plan, self._resources)

    def time_initialize_scopes(self) -> None:
        """Initialize scopes for 20 projects."""
        self._svc.initialize_scopes(self._plan, self._resources)

    def time_resolve_alias_last(self) -> None:
        """Resolve the last project by name (worst case)."""
        self._resolver.resolve_alias(self._plan.project_links, "proj-0019")

    def time_validate_cross_project(self) -> None:
        """Validate cross-project constraints for 20 projects."""
        self._svc.validate_cross_project(self._initialized)


class MultiProjectLargeSuite:
    """Benchmark multi-project operations with 50 projects."""

    def setup(self) -> None:
        self._svc = MultiProjectService(decision_service=_mock_decision_service())
        self._plan, self._resources = _build_plan(50)

    def time_initialize_scopes(self) -> None:
        """Initialize scopes for 50 projects."""
        self._svc.initialize_scopes(self._plan, self._resources)

    def time_get_project_scope(self) -> None:
        """Look up a project scope after initialization."""
        initialized = self._svc.initialize_scopes(self._plan, self._resources)
        initialized.get_project_scope("proj-0025")
