"""Performance benchmarks for graph decision chain queries.

Phase 13b: Validates json_extract() performance in recursive CTEs.
If average decision_chain() latency exceeds 50ms, documents v9 migration need.
"""

from __future__ import annotations

import sqlite3
import time
from datetime import UTC, datetime, timedelta

import pytest

from claude_engram.graph import decision_chain, record_outcome


@pytest.fixture()
def perf_db(tmp_path) -> sqlite3.Connection:
    """Create a v8 database with synthetic decision→outcome chains."""
    db_path = tmp_path / "perf_test.db"
    conn = sqlite3.connect(str(db_path))
    conn.row_factory = sqlite3.Row
    conn.executescript("""
        CREATE TABLE memories (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            content TEXT NOT NULL,
            category TEXT DEFAULT 'general',
            metadata TEXT DEFAULT '{}',
            created_at TEXT NOT NULL,
            updated_at TEXT NOT NULL,
            proj_tag TEXT DEFAULT '',
            summary TEXT DEFAULT NULL,
            is_active INTEGER DEFAULT 1,
            superseded_by INTEGER DEFAULT NULL,
            valid_from TEXT DEFAULT NULL,
            valid_to TEXT DEFAULT NULL,
            token_count INTEGER DEFAULT NULL
        );
        CREATE TABLE memory_relations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            source_id INTEGER NOT NULL,
            target_id INTEGER NOT NULL,
            relation_type TEXT NOT NULL,
            metadata TEXT DEFAULT '{}',
            created_at TEXT NOT NULL,
            valid_from TEXT DEFAULT NULL,
            valid_to TEXT DEFAULT NULL,
            invalidated_at TEXT DEFAULT NULL,
            UNIQUE(source_id, target_id, relation_type)
        );
        CREATE INDEX idx_relations_source ON memory_relations(source_id, relation_type);
        CREATE INDEX idx_relations_target ON memory_relations(target_id, relation_type);
    """)

    # Create 100 decision chains, depth=3 each → 400 memories, 300 edges
    base_time = datetime(2025, 1, 1, tzinfo=UTC)
    memory_id = 0

    for chain_idx in range(100):
        chain_ids = []
        for depth in range(4):  # decision + 3 outcomes
            memory_id += 1
            t = (base_time + timedelta(hours=memory_id)).isoformat()
            category = "decision" if depth == 0 else "outcome"
            conn.execute(
                "INSERT INTO memories (id, content, category, created_at, updated_at, "
                "summary, is_active, valid_from) VALUES (?, ?, ?, ?, ?, ?, 1, ?)",
                (
                    memory_id,
                    f"Chain {chain_idx} depth {depth} content",
                    category,
                    t,
                    t,
                    f"Chain {chain_idx} depth {depth} summary",
                    t,
                ),
            )
            chain_ids.append(memory_id)

        # Create resulted_in edges: decision→outcome1→outcome2→outcome3
        for i in range(len(chain_ids) - 1):
            record_outcome(
                conn,
                chain_ids[i],
                chain_ids[i + 1],
                confidence=0.8 - (i * 0.1),
                measurement_method="direct_observation",
                impact_score=0.7 - (i * 0.1),
            )

    conn.commit()
    return conn


class TestDecisionChainPerformance:
    def test_chain_query_under_50ms_average(self, perf_db: sqlite3.Connection) -> None:
        """decision_chain() with json_extract should average under 50ms per query."""
        decision_ids = list(range(1, 401, 4))  # IDs 1, 5, 9, ... (every 4th = decisions)

        start = time.perf_counter()
        for did in decision_ids:
            result = decision_chain(perf_db, did, depth=3, include_metadata=True)
            assert len(result["outcomes"]) == 3
        elapsed_ms = (time.perf_counter() - start) * 1000

        avg_ms = elapsed_ms / len(decision_ids)
        print(f"\nPerformance: {avg_ms:.2f}ms avg per query ({len(decision_ids)} queries)")
        print(f"Total: {elapsed_ms:.1f}ms for 100 chains × depth 3 with json_extract")

        assert avg_ms < 50, (
            f"Average decision_chain latency {avg_ms:.2f}ms exceeds 50ms threshold. "
            "Consider v9 migration with indexed columns."
        )

    def test_reverse_chain_performance(self, perf_db: sqlite3.Connection) -> None:
        """Incoming direction (outcome→decisions) should also be performant."""
        # Query the last outcome in each chain (IDs 4, 8, 12, ...)
        outcome_ids = list(range(4, 401, 4))

        start = time.perf_counter()
        for oid in outcome_ids:
            result = decision_chain(perf_db, oid, depth=3, direction="incoming")
            assert len(result["outcomes"]) == 3
        elapsed_ms = (time.perf_counter() - start) * 1000

        avg_ms = elapsed_ms / len(outcome_ids)
        print(f"\nReverse performance: {avg_ms:.2f}ms avg per query")
        assert avg_ms < 50

    def test_metadata_extraction_correctness(self, perf_db: sqlite3.Connection) -> None:
        """Verify json_extract returns correct values across many chains."""
        for chain_start in range(1, 20, 4):  # First 5 chains
            result = decision_chain(perf_db, chain_start, depth=3)
            for outcome in result["outcomes"]:
                assert outcome["confidence"] is not None
                assert 0.0 <= outcome["confidence"] <= 1.0
                assert outcome["impact_score"] is not None
                assert outcome["measurement_method"] == "direct_observation"
