"""ASV benchmarks for resource registry migration and ORM operations.

Measures:
- Schema creation time (Base.metadata.create_all)
- ResourceTypeModel insert throughput
- ResourceModel insert throughput
- ResourceEdgeModel insert throughput
- DAG query performance (children/parents lookup)
"""

from __future__ import annotations

import json
from datetime import UTC, datetime

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from cleveragents.infrastructure.database.models import (
    Base,
    ResourceEdgeModel,
    ResourceModel,
    ResourceTypeModel,
)


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


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


class SchemaCreation:
    """Benchmark schema creation (all tables via metadata.create_all)."""

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


class ResourceTypeInsert:
    """Benchmark inserting resource type records."""

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

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

    def time_insert_single_resource_type(self) -> None:
        self._iter += 1
        session = self.session_factory()
        rt = ResourceTypeModel()
        rt.name = f"bench/type-single-{self._iter}"
        rt.namespace = "bench"
        rt.resource_kind = "physical"
        rt.user_addable = True
        rt.created_at = _now_iso()
        rt.updated_at = _now_iso()
        session.add(rt)
        session.commit()
        session.close()

    def time_insert_100_resource_types(self) -> None:
        self._iter += 1
        batch = self._iter
        session = self.session_factory()
        for i in range(100):
            rt = ResourceTypeModel()
            rt.name = f"bench/type-{batch}-{i}"
            rt.namespace = "bench"
            rt.resource_kind = "physical" if i % 2 == 0 else "virtual"
            rt.user_addable = i % 3 == 0
            rt.args_schema_json = json.dumps([{"flag": f"--arg-{batch}-{i}"}])
            rt.created_at = _now_iso()
            rt.updated_at = _now_iso()
            session.add(rt)
        session.commit()
        session.close()


class ResourceInsert:
    """Benchmark inserting resource records."""

    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()
        rt = ResourceTypeModel()
        rt.name = "bench/git-checkout"
        rt.namespace = "bench"
        rt.resource_kind = "physical"
        rt.user_addable = True
        rt.created_at = _now_iso()
        rt.updated_at = _now_iso()
        session.add(rt)
        session.commit()
        session.close()
        self._iter = 0

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

    def time_insert_100_resources(self) -> None:
        self._iter += 1
        batch = self._iter
        session = self.session_factory()
        for i in range(100):
            idx = batch * 1000 + i
            r = ResourceModel()
            r.resource_id = _make_ulid(idx)
            r.namespaced_name = f"bench/resource-{batch}-{i}"
            r.namespace = "bench"
            r.type_name = "bench/git-checkout"
            r.resource_kind = "physical"
            r.location = f"/path/to/resource-{idx}"
            r.properties_json = json.dumps({"index": idx})
            r.created_at = _now_iso()
            r.updated_at = _now_iso()
            session.add(r)
        session.commit()
        session.close()


class ResourceEdgeQuery:
    """Benchmark DAG edge queries (children/parents lookup)."""

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

        # Create types
        for tname in ("bench/parent-type", "bench/child-type"):
            rt = ResourceTypeModel()
            rt.name = tname
            rt.namespace = "bench"
            rt.resource_kind = "physical"
            rt.user_addable = True
            rt.created_at = _now_iso()
            rt.updated_at = _now_iso()
            session.add(rt)

        # Create 1 parent + 50 children with edges
        parent = ResourceModel()
        parent.resource_id = _make_ulid(0)
        parent.namespaced_name = "bench/parent"
        parent.namespace = "bench"
        parent.type_name = "bench/parent-type"
        parent.resource_kind = "physical"
        parent.created_at = _now_iso()
        parent.updated_at = _now_iso()
        session.add(parent)
        self.parent_id = parent.resource_id

        for i in range(1, 51):
            child = ResourceModel()
            child.resource_id = _make_ulid(i)
            child.type_name = "bench/child-type"
            child.resource_kind = "physical"
            child.created_at = _now_iso()
            child.updated_at = _now_iso()
            session.add(child)

            edge = ResourceEdgeModel()
            edge.parent_id = parent.resource_id
            edge.child_id = child.resource_id
            edge.link_type = "contains"
            edge.created_at = _now_iso()
            session.add(edge)

        session.commit()
        session.close()

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

    def time_query_50_children(self) -> None:
        session = self.session_factory()
        edges = (
            session.query(ResourceEdgeModel)
            .filter(ResourceEdgeModel.parent_id == self.parent_id)
            .all()
        )
        assert len(edges) == 50
        session.close()

    def time_query_parent_of_child(self) -> None:
        session = self.session_factory()
        child_id = _make_ulid(25)
        edges = (
            session.query(ResourceEdgeModel)
            .filter(ResourceEdgeModel.child_id == child_id)
            .all()
        )
        assert len(edges) == 1
        session.close()
