from goodnews import summarize, share
from goodnews.db import connect, init_db


def test_parse_explain_labels():
    txt = ("WHAT: A town planted 500 trees.\n"
           "MATTERS: It cools streets and cuts flooding.\n"
           "BELONGS: Neighbors did it themselves — local agency and clear benefit.")
    ex = summarize._parse_explain(txt)
    assert ex["what_happened"].startswith("A town planted")
    assert "cools streets" in ex["why_matters"]
    assert "local agency" in ex["why_belongs"]


def test_parse_explain_partial():
    ex = summarize._parse_explain("WHAT: only this")
    assert ex["what_happened"] == "only this" and ex["why_matters"] is None and ex["why_belongs"] is None


def _seed_article(c):
    c.execute("INSERT INTO sources (id,name,feed_url) VALUES (1,'S','http://s/f')")
    c.execute("INSERT INTO articles (id,source_id,canonical_url,title,url_hash) VALUES (1,1,'http://a/1','Town plants trees','h')")
    c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (1,1)")
    c.commit()


def test_get_explanation_requires_all_three(tmp_path):
    c = connect(str(tmp_path / "e.db")); init_db(c); _seed_article(c)
    c.execute("INSERT INTO article_summaries (article_id,summary,what_happened) VALUES (1,'s','w')"); c.commit()
    assert summarize.get_explanation(c, 1) is None      # partial → fall back
    c.execute("UPDATE article_summaries SET why_matters='m', why_belongs='b' WHERE article_id=1"); c.commit()
    assert summarize.get_explanation(c, 1) == {"what_happened": "w", "why_matters": "m", "why_belongs": "b"}


def test_generate_summary_stores_explanation(tmp_path, monkeypatch):
    c = connect(str(tmp_path / "g.db")); init_db(c); _seed_article(c)

    class FakeClient:
        model = "test"
        def chat_text(self, messages):
            if "three" in messages[0]["content"].lower():
                return "WHAT: trees planted.\nMATTERS: cooler streets.\nBELONGS: local agency, clear benefit."
            return "A town planted trees, cooling streets."

    monkeypatch.setattr(summarize, "_fetch_text", lambda url: "body text")
    s = summarize.generate_summary(c, 1, client=FakeClient())
    assert s and "planted trees" in s
    ex = summarize.get_explanation(c, 1)
    assert "agency" in ex["why_belongs"] and "cooler streets" in ex["why_matters"]


def test_share_renders_structured_or_fallback():
    art = {"id": 1, "title": "T", "source_name": "Src", "source_id": 1,
           "canonical_url": "http://x", "reason_text": "calm reason", "tags": ""}
    full = share.render_share_page(art, "http://b", summary="sum",
                                   explanation={"what_happened": "WH", "why_matters": "WM", "why_belongs": "WB"})
    assert "What happened" in full and "Why it matters" in full and "Why it belongs here" in full
    assert "WH" in full and "WB" in full
    assert "fetch('/api/summary/'" not in full   # both cached → no poll needed
    # no explanation → the single "Why it’s here" reason line is the calm fallback
    fb = share.render_share_page(art, "http://b", summary="sum", explanation=None)
    assert "Why it’s here" in fb and "calm reason" in fb


def test_generate_tops_up_partial_explanation(tmp_path, monkeypatch):
    c = connect(str(tmp_path / "p.db")); init_db(c); _seed_article(c)
    # an older row: summary + why_belongs only (what_happened/why_matters missing)
    c.execute("INSERT INTO article_summaries (article_id,summary,why_belongs) VALUES (1,'old summary','b')")
    c.commit()
    assert summarize.get_explanation(c, 1) is None       # incomplete → falls back

    calls = {"explain": 0}
    def fake_explain(client, t, s, b):
        calls["explain"] += 1
        return {"what_happened": "w", "why_matters": "m", "why_belongs": "b2"}
    monkeypatch.setattr(summarize, "explain_article", fake_explain)
    monkeypatch.setattr(summarize, "_fetch_text", lambda url: "body")

    class FakeClient:
        model = "test"
        def chat_text(self, messages): return "should not be used for summary (kept)"

    out = summarize.generate_summary(c, 1, client=FakeClient())
    assert out == "old summary"                          # existing summary reused, not regenerated
    assert calls["explain"] == 1                          # explanation WAS topped up
    assert summarize.get_explanation(c, 1) == {"what_happened": "w", "why_matters": "m", "why_belongs": "b2"}
