"""Tests for graphify.querylog."""
import json
import os
import pytest
from pathlib import Path

from graphify.querylog import log_query, nodes_from_result


# ---------------------------------------------------------------------------
# nodes_from_result
# ---------------------------------------------------------------------------

def test_nodes_from_result_parses_header():
    result = "Traversal: BFS depth=2 | Start: ['foo'] | 7 nodes found\n\nNODE foo"
    assert nodes_from_result(result) == 7


def test_nodes_from_result_singular():
    assert nodes_from_result("1 node found") == 1


def test_nodes_from_result_missing():
    assert nodes_from_result("no match here") is None


def test_nodes_from_result_empty():
    assert nodes_from_result("") is None


# ---------------------------------------------------------------------------
# log_query — basic write
# ---------------------------------------------------------------------------

def test_log_query_writes_jsonl(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="query", question="what is X", corpus="/some/graph.json",
              result="3 nodes found\nNODE a", duration_ms=12.5, mode="bfs", depth=2)

    lines = log_file.read_text().splitlines()
    assert len(lines) == 1
    rec = json.loads(lines[0])
    assert rec["kind"] == "query"
    assert rec["question"] == "what is X"
    assert rec["corpus"] == "/some/graph.json"
    assert rec["nodes_returned"] == 3
    assert rec["result_chars"] > 0
    assert rec["duration_ms"] == pytest.approx(12.5, abs=0.01)
    assert rec["mode"] == "bfs"
    assert "ts" in rec


def test_log_query_appends(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="query", question="q1", corpus="/g.json")
    log_query(kind="query", question="q2", corpus="/g.json")

    lines = log_file.read_text().splitlines()
    assert len(lines) == 2
    assert json.loads(lines[0])["question"] == "q1"
    assert json.loads(lines[1])["question"] == "q2"


# ---------------------------------------------------------------------------
# opt-out / opt-in
# ---------------------------------------------------------------------------

def test_disable_env(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "1")

    log_query(kind="query", question="q", corpus="/g.json")

    assert not log_file.exists()


def test_disable_env_true(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "true")

    log_query(kind="query", question="q", corpus="/g.json")

    assert not log_file.exists()


def test_responses_not_logged_by_default(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_RESPONSES", raising=False)

    log_query(kind="query", question="q", corpus="/g.json", result="NODE foo")

    rec = json.loads(log_file.read_text())
    assert "response" not in rec


def test_responses_optin(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_RESPONSES", "1")
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="query", question="q", corpus="/g.json", result="NODE foo bar")

    rec = json.loads(log_file.read_text())
    assert rec["response"] == "NODE foo bar"


# ---------------------------------------------------------------------------
# robustness — never raises
# ---------------------------------------------------------------------------

def test_log_never_raises(tmp_path, monkeypatch):
    # Point at a directory — open() for append will fail
    bad_path = tmp_path / "is_a_dir"
    bad_path.mkdir()
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(bad_path))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    # Must not raise
    log_query(kind="query", question="q", corpus="/g.json")


def test_log_creates_parent_dirs(tmp_path, monkeypatch):
    log_file = tmp_path / "deep" / "nested" / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="query", question="q", corpus="/g.json")

    assert log_file.exists()


# ---------------------------------------------------------------------------
# field coverage
# ---------------------------------------------------------------------------

def test_nodes_returned_inferred_from_result(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="query", question="q", corpus="/g.json",
              result="5 nodes found\nNODE a\nNODE b")

    rec = json.loads(log_file.read_text())
    assert rec["nodes_returned"] == 5


def test_explicit_nodes_returned_takes_precedence(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="path", question="A -> B", corpus="/g.json", nodes_returned=3)

    rec = json.loads(log_file.read_text())
    assert rec["nodes_returned"] == 3


def test_kind_mcp_query(tmp_path, monkeypatch):
    log_file = tmp_path / "q.log"
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
    monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)

    log_query(kind="mcp_query", question="q", corpus="/g.json")

    rec = json.loads(log_file.read_text())
    assert rec["kind"] == "mcp_query"


# ---------------------------------------------------------------------------
# #1797 — query log is opt-in (default OFF)
# ---------------------------------------------------------------------------

def _clear_log_env(monkeypatch):
    for k in ("GRAPHIFY_QUERY_LOG", "GRAPHIFY_QUERY_LOG_ENABLE", "GRAPHIFY_QUERY_LOG_DISABLE"):
        monkeypatch.delenv(k, raising=False)


def test_query_log_off_by_default(monkeypatch):
    from graphify.querylog import _log_path
    _clear_log_env(monkeypatch)
    assert _log_path() is None


def test_query_log_enabled_by_explicit_flag(monkeypatch):
    from graphify.querylog import _log_path
    _clear_log_env(monkeypatch)
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_ENABLE", "1")
    assert str(_log_path()).endswith("graphify-queries.log")


def test_query_log_enabled_by_explicit_path(monkeypatch, tmp_path):
    from graphify.querylog import _log_path
    _clear_log_env(monkeypatch)
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(tmp_path / "q.log"))
    assert _log_path() == tmp_path / "q.log"


def test_query_log_disable_wins(monkeypatch):
    from graphify.querylog import _log_path
    _clear_log_env(monkeypatch)
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_ENABLE", "1")
    monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "1")
    assert _log_path() is None


def test_log_query_writes_nothing_by_default(monkeypatch, tmp_path):
    """End-to-end: with no opt-in, log_query must not create the default log."""
    _clear_log_env(monkeypatch)
    monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
    log_query(kind="query", question="secret internal ticket TICKET-123", corpus=".", result="1 node found")
    assert not (tmp_path / ".cache" / "graphify-queries.log").exists()
