"""ASV benchmarks for project create + show round-trip.

Measures the cost of creating a project and then showing it via the
``NamespacedProjectRepository``, using a file-based SQLite database so
that each operation gets a fresh session (matching real CLI behaviour).

Targets bug #590 — ``create()`` now calls ``session.commit()`` so data
is visible to a subsequent ``get()`` in a separate session.
"""

from __future__ import annotations

import contextlib
import importlib
import os
import shutil
import sys
import tempfile
from pathlib import Path

# 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)

# Force-reload so ASV picks up the source tree version.
import cleveragents  # noqa: E402

importlib.reload(cleveragents)

from sqlalchemy import create_engine  # noqa: E402
from sqlalchemy.orm import sessionmaker  # noqa: E402
from sqlalchemy.pool import NullPool  # noqa: E402

from cleveragents.domain.models.core.project import (  # noqa: E402
    NamespacedProject,
    parse_namespaced_name,
)
from cleveragents.infrastructure.database.models import Base  # noqa: E402
from cleveragents.infrastructure.database.repositories import (  # noqa: E402
    NamespacedProjectRepository,
    ProjectNotFoundError,
)


def _fresh_repo(db_path: str) -> NamespacedProjectRepository:
    """Return a repo backed by a *new* engine/session for *db_path*.

    ``NullPool`` ensures SQLite file handles are released immediately
    when the engine is no longer referenced.
    """
    engine = create_engine(f"sqlite:///{db_path}", echo=False, poolclass=NullPool)
    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine, expire_on_commit=False)
    return NamespacedProjectRepository(session_factory=factory)


class ProjectShowAfterCreateSuite:
    """Benchmark create-then-show round trip with separate sessions."""

    timeout = 30.0

    def setup(self) -> None:
        self._tmpdir = tempfile.mkdtemp(prefix="bench_show_590_")
        self._db_path = os.path.join(self._tmpdir, "bench.db")
        engine = create_engine(
            f"sqlite:///{self._db_path}",
            echo=False,
            poolclass=NullPool,
        )
        Base.metadata.create_all(engine)
        engine.dispose()
        self._counter = 0

    def teardown(self) -> None:
        shutil.rmtree(self._tmpdir, ignore_errors=True)

    def time_create_then_show(self) -> None:
        """Create a project in one session, show (get) in another."""
        self._counter += 1
        name = f"bench-show-{self._counter}"
        parsed = parse_namespaced_name(name)
        proj = NamespacedProject(name=parsed.name, namespace=parsed.namespace)

        create_repo = _fresh_repo(self._db_path)
        create_repo.create(proj)

        show_repo = _fresh_repo(self._db_path)
        with contextlib.suppress(ProjectNotFoundError):
            show_repo.get(f"local/{name}")

    def track_show_after_create_found(self) -> int:
        """Track whether show finds the created project.

        Returns 1 when get() successfully finds the project; 0 if
        ProjectNotFoundError is raised.
        """
        self._counter += 1
        name = f"bench-show-track-{self._counter}"
        parsed = parse_namespaced_name(name)
        proj = NamespacedProject(name=parsed.name, namespace=parsed.namespace)

        create_repo = _fresh_repo(self._db_path)
        create_repo.create(proj)

        show_repo = _fresh_repo(self._db_path)
        try:
            show_repo.get(f"local/{name}")
            return 1
        except ProjectNotFoundError:
            return 0


ProjectShowAfterCreateSuite.track_show_after_create_found.unit = "found"
