"""ASV benchmarks for plan phase rebaseline migration (a5_005).

Measures:
- Schema creation time with rebaselined constraints
- Plan insert throughput with Action phase
- Plan insert throughput with Apply terminal states
"""

from __future__ import annotations

import threading
from datetime import UTC, datetime

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from cleveragents.infrastructure.database.models import (
    Base,
    LifecycleActionModel,
    LifecyclePlanModel,
)

# Thread-safe counter so repeated ASV iterations never collide on IDs.
_iter_lock = threading.Lock()
_iter_counter = 0


def _next_iter() -> int:
    """Return a monotonically increasing iteration number."""
    global _iter_counter
    with _iter_lock:
        _iter_counter += 1
        return _iter_counter


def _now_iso() -> str:
    return datetime.now(tz=UTC).isoformat()


def _make_ulid(n: int) -> str:
    return str(n).zfill(26)


class SchemaCreationWithRebaseline:
    """Benchmark schema creation including rebaselined v3_plans constraints."""

    def time_create_all_tables(self) -> None:
        engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(engine)
        engine.dispose()


class PlanInsertActionPhase:
    """Benchmark inserting plans with phase='action' (new phase)."""

    def setup(self) -> None:
        self.engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(self.engine)
        self.session_factory = sessionmaker(bind=self.engine)

        # Create parent action
        session = self.session_factory()
        action = LifecycleActionModel()
        action.namespaced_name = "bench/phase-action"
        action.namespace = "bench"
        action.name = "phase-action"
        action.description = "Benchmark action"
        action.definition_of_done = "Done"
        action.strategy_actor = "bench/s"
        action.execution_actor = "bench/e"
        action.state = "available"
        action.tags_json = "[]"
        action.created_at = _now_iso()
        action.updated_at = _now_iso()
        session.add(action)
        session.commit()
        session.close()

    def teardown(self) -> None:
        self.engine.dispose()

    def time_insert_100_action_phase_plans(self) -> None:
        it = _next_iter()
        base = it * 10000
        session = self.session_factory()
        now = _now_iso()
        for i in range(100):
            plan = LifecyclePlanModel()
            pid = _make_ulid(base + i)
            plan.plan_id = pid
            plan.root_plan_id = pid
            plan.action_name = "bench/phase-action"
            plan.namespaced_name = f"bench/plan-{it}-{i}"
            plan.namespace = "bench"
            plan.phase = "action"
            plan.processing_state = "queued"
            plan.description = f"Benchmark plan {it}-{i}"
            plan.tags_json = "[]"
            plan.effective_profile_snapshot = "{}"
            plan.created_at = now
            plan.updated_at = now
            session.add(plan)
        session.commit()
        session.close()


class PlanInsertApplyTerminalStates:
    """Benchmark inserting plans with Apply terminal states."""

    def setup(self) -> None:
        self.engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(self.engine)
        self.session_factory = sessionmaker(bind=self.engine)

        session = self.session_factory()
        action = LifecycleActionModel()
        action.namespaced_name = "bench/terminal-action"
        action.namespace = "bench"
        action.name = "terminal-action"
        action.description = "Benchmark terminal action"
        action.definition_of_done = "Done"
        action.strategy_actor = "bench/s"
        action.execution_actor = "bench/e"
        action.state = "available"
        action.tags_json = "[]"
        action.created_at = _now_iso()
        action.updated_at = _now_iso()
        session.add(action)
        session.commit()
        session.close()

    def teardown(self) -> None:
        self.engine.dispose()

    def time_insert_50_applied_plans(self) -> None:
        it = _next_iter()
        base = it * 10000
        session = self.session_factory()
        now = _now_iso()
        for i in range(50):
            plan = LifecyclePlanModel()
            pid = _make_ulid(base + i)
            plan.plan_id = pid
            plan.root_plan_id = pid
            plan.action_name = "bench/terminal-action"
            plan.namespaced_name = f"bench/applied-{it}-{i}"
            plan.namespace = "bench"
            plan.phase = "apply"
            plan.processing_state = "applied"
            plan.description = f"Applied plan {it}-{i}"
            plan.tags_json = "[]"
            plan.effective_profile_snapshot = "{}"
            plan.created_at = now
            plan.updated_at = now
            session.add(plan)
        session.commit()
        session.close()

    def time_insert_50_constrained_plans(self) -> None:
        it = _next_iter()
        base = it * 10000
        session = self.session_factory()
        now = _now_iso()
        for i in range(50):
            plan = LifecyclePlanModel()
            pid = _make_ulid(base + i)
            plan.plan_id = pid
            plan.root_plan_id = pid
            plan.action_name = "bench/terminal-action"
            plan.namespaced_name = f"bench/constrained-{it}-{i}"
            plan.namespace = "bench"
            plan.phase = "apply"
            plan.processing_state = "constrained"
            plan.description = f"Constrained plan {it}-{i}"
            plan.tags_json = "[]"
            plan.effective_profile_snapshot = "{}"
            plan.created_at = now
            plan.updated_at = now
            session.add(plan)
        session.commit()
        session.close()
