"""Tests for analytics query layer over SQLiteStore."""

from __future__ import annotations

from orxaq.fabric.analytics import (
    AggregateResult,
    AnalyticsEngine,
    QueryFilter,
    TimeSeriesBucket,
)
from orxaq.fabric.persistence import SQLiteStore


def _seeded_store() -> SQLiteStore:
    """Create a store pre-populated with test data."""
    store = SQLiteStore(":memory:")
    # Snapshots
    store._conn.execute(
        "INSERT INTO dag_snapshots (name, dag_json, metadata, created_at) VALUES (?, ?, ?, ?)",
        ("v1", '{"nodes":1}', '{"tag":"alpha"}', "2026-01-15T10:00:00Z"),
    )
    store._conn.execute(
        "INSERT INTO dag_snapshots (name, dag_json, metadata, created_at) VALUES (?, ?, ?, ?)",
        ("v2", '{"nodes":3}', '{"tag":"beta"}', "2026-01-16T14:30:00Z"),
    )
    store._conn.execute(
        "INSERT INTO dag_snapshots (name, dag_json, metadata, created_at) VALUES (?, ?, ?, ?)",
        ("v2", '{"nodes":5}', '{"tag":"beta"}', "2026-02-01T08:00:00Z"),
    )
    # Events
    store._conn.execute(
        "INSERT INTO audit_log (event_type, actor, details, created_at) VALUES (?, ?, ?, ?)",
        ("dag.create", "alice", "created dag v1", "2026-01-15T10:00:00Z"),
    )
    store._conn.execute(
        "INSERT INTO audit_log (event_type, actor, details, created_at) VALUES (?, ?, ?, ?)",
        ("dag.update", "bob", "added 2 nodes", "2026-01-16T14:30:00Z"),
    )
    store._conn.execute(
        "INSERT INTO audit_log (event_type, actor, details, created_at) VALUES (?, ?, ?, ?)",
        ("ecl.compute", "alice", "ECL run #42", "2026-02-01T08:00:00Z"),
    )
    store._conn.execute(
        "INSERT INTO audit_log (event_type, actor, details, created_at) VALUES (?, ?, ?, ?)",
        ("dag.update", "alice", "edge fix", "2026-02-01T09:00:00Z"),
    )
    # KV pairs
    store._conn.execute(
        "INSERT INTO kv_store (key, value, updated_at) VALUES (?, ?, ?)",
        ("config.mode", "production", "2026-01-15T10:00:00Z"),
    )
    store._conn.execute(
        "INSERT INTO kv_store (key, value, updated_at) VALUES (?, ?, ?)",
        ("config.debug", "false", "2026-01-15T10:00:00Z"),
    )
    store._conn.execute(
        "INSERT INTO kv_store (key, value, updated_at) VALUES (?, ?, ?)",
        ("model.version", "3", "2026-02-01T08:00:00Z"),
    )
    store._conn.commit()
    return store


# ── QueryFilter defaults ────────────────────────────────


class TestQueryFilter:
    def test_defaults(self):
        f = QueryFilter()
        assert f.start_date is None
        assert f.end_date is None
        assert f.event_types == []
        assert f.actors == []
        assert f.name_pattern is None
        assert f.metadata_tags == {}
        assert f.limit == 100
        assert f.offset == 0


# ── Snapshot queries ────────────────────────────────────


class TestSnapshotQueries:
    def test_query_all(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        results = engine.query_snapshots()
        assert len(results) == 3

    def test_query_by_date_range(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(
            start_date="2026-01-16T00:00:00Z",
            end_date="2026-01-16T23:59:59Z",
        )
        results = engine.query_snapshots(filt)
        assert len(results) == 1
        assert results[0]["name"] == "v2"

    def test_query_by_name_pattern(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(name_pattern="v1")
        results = engine.query_snapshots(filt)
        assert len(results) == 1
        assert results[0]["name"] == "v1"

    def test_query_by_metadata_tag(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(metadata_tags={"tag": "beta"})
        results = engine.query_snapshots(filt)
        assert len(results) == 2
        assert all(r["metadata"]["tag"] == "beta" for r in results)

    def test_query_with_offset(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(limit=1, offset=1)
        results = engine.query_snapshots(filt)
        assert len(results) == 1

    def test_snapshot_count(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        assert engine.snapshot_count() == 3

    def test_snapshot_names(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        names = engine.snapshot_names()
        assert names == ["v1", "v2"]

    def test_empty_store_snapshots(self):
        store = SQLiteStore(":memory:")
        engine = AnalyticsEngine(store)
        assert engine.query_snapshots() == []
        assert engine.snapshot_count() == 0
        assert engine.snapshot_names() == []


# ── Event queries ───────────────────────────────────────


class TestEventQueries:
    def test_query_all(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        results = engine.query_events()
        assert len(results) == 4

    def test_query_by_event_type(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(event_types=["dag.update"])
        results = engine.query_events(filt)
        assert len(results) == 2
        assert all(r["event_type"] == "dag.update" for r in results)

    def test_query_by_actor(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(actors=["bob"])
        results = engine.query_events(filt)
        assert len(results) == 1
        assert results[0]["actor"] == "bob"

    def test_query_by_multiple_types(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(event_types=["dag.create", "ecl.compute"])
        results = engine.query_events(filt)
        assert len(results) == 2

    def test_query_by_date_range(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(start_date="2026-02-01T00:00:00Z")
        results = engine.query_events(filt)
        assert len(results) == 2

    def test_event_count_total(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        assert engine.event_count() == 4

    def test_event_count_by_type(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        assert engine.event_count("dag.update") == 2
        assert engine.event_count("ecl.compute") == 1

    def test_empty_store_events(self):
        store = SQLiteStore(":memory:")
        engine = AnalyticsEngine(store)
        assert engine.query_events() == []
        assert engine.event_count() == 0


# ── Aggregation ─────────────────────────────────────────


class TestAggregation:
    def test_aggregate_events_by_type(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        result = engine.aggregate_events("event_type")
        assert result.count == 4
        assert result.groups["dag.update"] == 2
        assert result.groups["dag.create"] == 1
        assert result.groups["ecl.compute"] == 1

    def test_aggregate_events_by_actor(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        result = engine.aggregate_events("actor")
        assert result.count == 4
        assert result.groups["alice"] == 3
        assert result.groups["bob"] == 1

    def test_aggregate_events_with_date_filter(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(start_date="2026-02-01T00:00:00Z")
        result = engine.aggregate_events("event_type", filt)
        assert result.count == 2
        assert "dag.create" not in result.groups

    def test_aggregate_invalid_group_by(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        try:
            engine.aggregate_events("invalid_column")
            assert False, "Expected ValueError"
        except ValueError as e:
            assert "group_by must be" in str(e)

    def test_aggregate_snapshots_by_name(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        result = engine.aggregate_snapshots()
        assert result.count == 3
        assert result.groups["v2"] == 2
        assert result.groups["v1"] == 1

    def test_aggregate_result_to_dict(self):
        r = AggregateResult(count=5, groups={"a": 3, "b": 2})
        d = r.to_dict()
        assert d["count"] == 5
        assert d["groups"]["a"] == 3

    def test_aggregate_result_empty(self):
        r = AggregateResult()
        d = r.to_dict()
        assert d["count"] == 0
        assert "groups" not in d


# ── Time-series ─────────────────────────────────────────


class TestTimeSeries:
    def test_event_timeline_by_day(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        buckets = engine.event_timeline("day")
        assert len(buckets) == 3
        assert buckets[0].period == "2026-01-15"
        assert buckets[0].count == 1
        assert buckets[2].period == "2026-02-01"
        assert buckets[2].count == 2

    def test_event_timeline_by_hour(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        buckets = engine.event_timeline("hour")
        assert len(buckets) == 4  # 4 distinct hours
        assert buckets[0].period == "2026-01-15T10"

    def test_event_timeline_with_filter(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(event_types=["dag.update"])
        buckets = engine.event_timeline("day", filt)
        assert len(buckets) == 2

    def test_event_timeline_invalid_granularity(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        try:
            engine.event_timeline("week")
            assert False, "Expected ValueError"
        except ValueError as e:
            assert "granularity must be" in str(e)

    def test_snapshot_timeline_by_day(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        buckets = engine.snapshot_timeline("day")
        assert len(buckets) == 3
        periods = [b.period for b in buckets]
        assert "2026-01-15" in periods
        assert "2026-02-01" in periods

    def test_snapshot_timeline_invalid_granularity(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        try:
            engine.snapshot_timeline("week")
            assert False, "Expected ValueError"
        except ValueError as e:
            assert "granularity must be" in str(e)

    def test_time_series_bucket_stats(self):
        b = TimeSeriesBucket(period="2026-01-15", count=3, values=[1.0, 2.0, 3.0])
        assert b.total == 6.0
        assert b.mean == 2.0
        d = b.to_dict()
        assert d["period"] == "2026-01-15"
        assert d["count"] == 3
        assert d["total"] == 6.0

    def test_time_series_bucket_empty(self):
        b = TimeSeriesBucket(period="2026-01-01", count=0)
        assert b.total == 0.0
        assert b.mean == 0.0


# ── KV analytics ────────────────────────────────────────


class TestKVAnalytics:
    def test_kv_stats(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        stats = engine.kv_stats()
        assert stats["total_keys"] == 3
        assert stats["total_bytes"] > 0
        assert stats["prefix_distribution"]["config"] == 2
        assert stats["prefix_distribution"]["model"] == 1

    def test_kv_stats_empty(self):
        store = SQLiteStore(":memory:")
        engine = AnalyticsEngine(store)
        stats = engine.kv_stats()
        assert stats["total_keys"] == 0
        assert stats["total_bytes"] == 0


# ── Search ──────────────────────────────────────────────


class TestSearch:
    def test_search_events(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        results = engine.search_events("ECL")
        assert len(results) == 1
        assert "ECL" in results[0]["details"]

    def test_search_no_match(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        results = engine.search_events("nonexistent_term_xyz")
        assert len(results) == 0

    def test_search_case_insensitive_like(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        # SQLite LIKE is case-insensitive for ASCII by default
        results = engine.search_events("ecl")
        assert len(results) == 1


# ── Summary ─────────────────────────────────────────────


class TestSummary:
    def test_summary(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        s = engine.summary()
        assert s["snapshots"] == 3
        assert s["events"] == 4
        assert len(s["snapshot_names"]) == 2
        assert s["kv"]["total_keys"] == 3

    def test_summary_empty(self):
        store = SQLiteStore(":memory:")
        engine = AnalyticsEngine(store)
        s = engine.summary()
        assert s["snapshots"] == 0
        assert s["events"] == 0
        assert s["kv"]["total_keys"] == 0


# ── Additional coverage: end_date filters, numeric_stats ──


class TestEndDateFilters:
    """Cover end_date branches in query_events, aggregate_events, aggregate_snapshots."""

    def test_query_events_end_date(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(end_date="2026-01-16T23:59:59Z")
        results = engine.query_events(filt)
        assert len(results) == 2  # Only Jan 15 and Jan 16 events.

    def test_aggregate_events_end_date(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(end_date="2026-01-31T23:59:59Z")
        result = engine.aggregate_events("event_type", filt)
        assert result.count == 2

    def test_aggregate_snapshots_date_range(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(
            start_date="2026-01-16T00:00:00Z",
            end_date="2026-01-31T23:59:59Z",
        )
        result = engine.aggregate_snapshots(filt)
        assert result.count == 1

    def test_event_timeline_date_range(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        filt = QueryFilter(
            start_date="2026-01-15T00:00:00Z",
            end_date="2026-01-16T23:59:59Z",
        )
        buckets = engine.event_timeline("day", filt)
        total = sum(b.count for b in buckets)
        assert total == 2

    def test_snapshot_timeline_hour(self):
        store = _seeded_store()
        engine = AnalyticsEngine(store)
        buckets = engine.snapshot_timeline("hour")
        assert len(buckets) >= 2
        # Hour granularity uses SUBSTR(,1,13).
        assert "T" in buckets[0].period

    def test_aggregate_result_with_numeric_stats(self):
        r = AggregateResult(count=3, numeric_stats={"mean": 2.5, "max": 5.0})
        d = r.to_dict()
        assert d["stats"]["mean"] == 2.5
        assert d["stats"]["max"] == 5.0
