"""Tests for the NL-query Matrix surface: trigger detection + answer rendering (pure, no network)."""
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import query  # noqa: E402


# ── parse_trigger ───────────────────────────────────────────────────────────────────────
def test_trigger_question_mark():
    assert query.parse_trigger("?who are our top investors") == "who are our top investors"
    assert query.parse_trigger("  ? spaced out ") == "spaced out"


def test_trigger_at_bot():
    assert query.parse_trigger("@bot top 10 investors") == "top 10 investors"
    assert query.parse_trigger("@bot: top 10 investors") == "top 10 investors"   # pill-style colon
    assert query.parse_trigger("@BOT  spaced") == "spaced"                       # case-insensitive


def test_trigger_slash_forms():
    assert query.parse_trigger("/ask when did we last email Acme?") == "when did we last email Acme?"
    assert query.parse_trigger("/query top investors") == "top investors"
    assert query.parse_trigger("/q top investors") == "top investors"


def test_trigger_bare_returns_empty_string():
    # A bare trigger is matched (so we show help) but carries no question.
    assert query.parse_trigger("@bot") == ""
    assert query.parse_trigger("?") == ""


def test_non_trigger_routes_to_intake():
    assert query.parse_trigger("New investor: Acme — Jane <jane@acme.com>") is None
    # 'ask' as a note verb must NOT trigger (would collide with real intake notes).
    assert query.parse_trigger("Ask Jane to send the Q3 deck") is None
    assert query.parse_trigger("/asking for a friend") is None  # needs a separator after /ask
    assert query.parse_trigger("") is None
    assert query.parse_trigger("   ") is None


# ── render_answer ───────────────────────────────────────────────────────────────────────
def test_render_scalar_rows():
    out = query.render_answer({
        "intent": "top_investors_committed", "slots": {"limit": 2},
        "summary": "Top 2 investor(s) by committed capital.",
        "columns": ["investor_name", "total_invested", "lead"],
        "rows": [{"investor_name": "Acme Capital", "total_invested": 5000000, "lead": "Grant"},
                 {"investor_name": "Beta Fund", "total_invested": 2500000, "lead": "Jonathan"}],
        "truncated": False})
    assert "Top 2 investor(s)" in out
    assert "**Acme Capital**" in out
    assert "$5,000,000" in out                       # money formatting
    assert "read as: top_investors_committed" in out  # interpretation footer


def test_render_nested_contacts_and_commitments():
    out = query.render_answer({
        "intent": "investor_lookup", "slots": {"name": "Acme"},
        "summary": '1 investor(s) matching "Acme".',
        "columns": ["investor_name", "lead", "total_invested", "graveyard", "contacts", "commitments"],
        "rows": [{"investor_name": "Acme Capital", "lead": "Grant", "total_invested": 5000000,
                  "graveyard": 0,
                  "contacts": [{"full_name": "Jane Doe", "email": "jane@acme.com", "title": "GP",
                                "city": "Austin", "state": "TX", "country": ""}],
                  "commitments": [{"fund_name": "Fund I", "amount": 5000000}]}],
        "truncated": False})
    assert "Jane Doe <jane@acme.com>" in out
    assert "Fund I: $5,000,000" in out
    assert "graveyard" not in out  # 0-valued flag column suppressed


def test_render_flag_when_set():
    out = query.render_answer({
        "intent": "investors_follow_up", "slots": {},
        "summary": "1 investor(s) with an open follow-up reminder.",
        "columns": ["investor_name", "title", "due_date", "status", "overdue"],
        "rows": [{"investor_name": "Acme", "title": "Send deck", "due_date": "2026-01-01",
                  "status": "open", "overdue": 1}]})
    assert "⚠️ overdue" in out
    assert "2026-01-01" in out  # date truncated to YYYY-MM-DD


def test_render_no_rows():
    out = query.render_answer({"intent": "investors_by_city", "slots": {"city": "Nowhere"},
                               "summary": '0 investor contact(s) in "Nowhere".',
                               "columns": [], "rows": []})
    assert "no matching" in out.lower()


def test_render_overflow_note():
    rows = [{"investor_name": f"Inv {i}", "total_invested": i}
            for i in range(query.MAX_DISPLAY_ROWS + 5)]
    out = query.render_answer({"intent": "top_investors_committed", "slots": {}, "summary": "many",
                               "columns": ["investor_name", "total_invested"], "rows": rows})
    assert "+5 more not shown" in out


def test_render_errors():
    assert "couldn't map" in query.render_answer({"error": "no_match", "question": "huh"}).lower()
    assert "unreachable" in query.render_answer({"error": "model_unavailable"}).lower()
    assert "failed" in query.render_answer({"error": "query_failed", "detail": "boom"}).lower()
    assert "bad_slot" in query.render_answer({"error": "bad_slot", "detail": "x"})


if __name__ == "__main__":
    fns = [v for k, v in sorted(globals().items()) if k.startswith("test_") and callable(v)]
    for fn in fns:
        fn()
        print(f"ok  {fn.__name__}")
    print(f"\n{len(fns)} passed")
