"""Offline tests for the email-proposal review logic (card render, framing, reply grammar, note
revision). The network/Matrix wiring lives in bot.py (live-smoke only); this covers pure functions."""
import os
import sys

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

import email_proposals  # noqa: E402

ITEM = {
    "id": "p1", "investor_name": "Acme Capital", "direction": "received",
    "from_name": "Jane Doe", "from_email": "jane@acme.com",
    "email_subject": "Re: Fund III", "email_date": "2026-06-02",
    "snippet": "thanks for the deck — one question on terms",
    "proposed_note": "✉ Jane Doe emailed the team: asked about terms",
}


def test_interpret_yes_no_else():
    assert email_proposals.interpret("yes") == "approve"
    assert email_proposals.interpret(" Y ") == "approve"
    assert email_proposals.interpret("✅") == "approve"
    assert email_proposals.interpret("no") == "reject"
    assert email_proposals.interpret("skip") == "reject"
    # anything that isn't a clear yes/no is treated as a revision instruction
    assert email_proposals.interpret("say we discussed the Q3 raise") == "revise"


def test_frame_wraps_with_rules():
    out = email_proposals.frame("hello")
    lines = out.split("\n")
    assert lines[0] == email_proposals.RULE and lines[-1] == email_proposals.RULE
    assert "hello" in out


def test_render_card_has_context_note_and_actions():
    card = email_proposals.render_card(ITEM)
    assert "Acme Capital" in card
    assert "Jane Doe" in card
    assert "Re: Fund III" in card and "2026-06-02" in card
    assert "thanks for the deck" in card
    assert "Jane Doe emailed the team: asked about terms" in card  # the clear, named note
    assert "yes" in card.lower() and "no" in card.lower()


def test_render_card_is_framed_and_dropless_direction():
    card = email_proposals.render_card(ITEM)
    assert card.startswith(email_proposals.RULE) and card.rstrip().endswith(email_proposals.RULE)
    # the bare Sent/Received label is gone — the note itself names who emailed whom
    assert "(Received)" not in card and "(Sent)" not in card


def test_render_card_truncates_long_snippet():
    card = email_proposals.render_card(dict(ITEM, snippet="x" * 1000))
    assert "…" in card and len(card) < 1000


def test_revise_note_applies_model_output():
    out = email_proposals.revise_note(
        "old note", "make it about the Q3 raise",
        parse_fn=lambda prompt, system=None, max_tokens=400: {"note": "Discussed the Q3 raise."})
    assert out == "Discussed the Q3 raise."


def test_revise_note_noop_or_empty_returns_none():
    # model echoes the same note unchanged -> None so the caller re-prompts (not "Updated")
    assert email_proposals.revise_note("same", "x", parse_fn=lambda *a, **k: {"note": "same"}) is None
    # model returns nothing usable -> None
    assert email_proposals.revise_note("n", "y", parse_fn=lambda *a, **k: {}) is None
    assert email_proposals.revise_note("n", "y", parse_fn=lambda *a, **k: None) is None


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