from goodnews.db import connect, init_db
from goodnews import digest, email_send


def _seed(c, n=5, date="2026-06-09"):
    bid = c.execute("INSERT INTO daily_briefs (brief_date, title) VALUES (?, 't')", (date,)).lastrowid
    for i in range(1, n + 1):
        c.execute("INSERT INTO sources (id,name,feed_url,active,content_visible) VALUES (?,?,?,1,1)",
                  (i, f"Src{i}", f"http://s{i}/f"))
        c.execute("INSERT INTO articles (id,source_id,canonical_url,title,url_hash) VALUES (?,?,?,?,?)",
                  (i, i, f"http://a/{i}", f"Title {i}", f"h{i}"))
        c.execute("INSERT INTO article_scores (article_id,accepted,reason_text) VALUES (?,1,?)", (i, f"reason {i}"))
        c.execute("INSERT INTO article_summaries (article_id,summary) VALUES (?,?)", (i, f"summary {i}"))
        c.execute("INSERT INTO daily_brief_items (brief_id,article_id,rank) VALUES (?,?,?)", (bid, i, i))
    c.execute("INSERT INTO users (id,email,digest_enabled) VALUES (1,'reader@x.com',1)")
    c.commit()
    return date


def test_build_digest_is_calm_and_dated():
    items = [{"id": 1, "title": "Good thing", "canonical_url": "http://a/1", "source": "Src", "summary": "nice", "reason_text": "wonder", "paywalled": False}]
    subject, text, html = digest.build_digest(items, "2026-06-09", "http://ub/unsub")
    assert "Tuesday's upbeatBytes" in subject and "1 calm read" in subject
    assert "Daily Highlights" in text and "Daily Highlights" in html
    assert "more good news is always" in text and "http://ub/unsub" in text  # points back to the site
    assert "Good thing" in html and "Read on upbeatBytes" in html and "Unsubscribe" in html
    assert "you missed" not in (text + html).lower()  # no guilt language


def test_send_due_digests_sends_dedupes_and_skips(monkeypatch, tmp_path):
    sent = []
    monkeypatch.setattr(email_send, "send_email",
                        lambda to, subj, text, html=None, headers=None, **k: sent.append((to, headers)))
    c = connect(str(tmp_path / "d.db")); init_db(c)
    date = _seed(c, n=5)
    monkeypatch.setattr(digest, "local_today", lambda: date)
    # force=True bypasses the morning window
    assert digest.send_due_digests(c, force=True) == 1
    assert sent[0][0] == "reader@x.com"
    hdrs = sent[0][1]  # RFC 8058 one-click unsubscribe headers present
    assert "List-Unsubscribe" in hdrs and hdrs["List-Unsubscribe-Post"] == "List-Unsubscribe=One-Click"
    assert c.execute("SELECT item_count FROM digest_sends WHERE user_id=1").fetchone()[0] == 5
    # dedupe: a second run sends nothing
    assert digest.send_due_digests(c, force=True) == 0
    # unsub token was generated on send
    assert c.execute("SELECT digest_unsub_token FROM users WHERE id=1").fetchone()[0]


def test_send_skips_thin_day(monkeypatch, tmp_path):
    monkeypatch.setattr(email_send, "send_email", lambda *a, **k: (_ for _ in ()).throw(AssertionError("should not send")))
    c = connect(str(tmp_path / "d.db")); init_db(c)
    date = _seed(c, n=3)  # below MIN_ITEMS (4)
    monkeypatch.setattr(digest, "local_today", lambda: date)
    assert digest.send_due_digests(c, force=True) == 0


def test_send_respects_opt_out(monkeypatch, tmp_path):
    sent = []
    monkeypatch.setattr(email_send, "send_email", lambda to, *a, **k: sent.append(to))
    c = connect(str(tmp_path / "d.db")); init_db(c)
    date = _seed(c, n=5)
    c.execute("UPDATE users SET digest_enabled=0 WHERE id=1"); c.commit()
    monkeypatch.setattr(digest, "local_today", lambda: date)
    assert digest.send_due_digests(c, force=True) == 0 and sent == []


def test_followed_digest_items_caps_excludes_and_section(tmp_path):
    c = connect(str(tmp_path / "fd.db")); init_db(c)
    date = _seed(c, n=5)  # brief sources/articles 1..5 + user 1
    # two NON-brief accepted articles from followed source 1 → cap should keep 1
    for aid in (10, 11):
        c.execute("INSERT INTO articles (id,source_id,canonical_url,title,url_hash) VALUES (?,1,?,?,?)",
                  (aid, f"http://a/{aid}", f"Followed {aid}", f"h{aid}"))
        c.execute("INSERT INTO article_scores (article_id,accepted) VALUES (?,1)", (aid,))
    c.execute("INSERT INTO user_follows (user_id,kind,value) VALUES (1,'source','1')")
    c.commit()
    fol = digest.followed_digest_items(c, 1, exclude_ids=[1, 2, 3, 4, 5], limit=3)
    assert len(fol) == 1 and fol[0]["source_id"] == 1     # one-per-source cap, brief ids excluded
    assert fol[0]["id"] in (10, 11)
    # section appears with followed items, omitted without (brief stays the star)
    brief = [{"id": 1, "title": "B", "canonical_url": "http://b/1", "source": "S", "summary": "s", "reason_text": "r", "paywalled": False}]
    _, text, html = digest.build_digest(brief, "2026-06-09", "http://u", followed=fol)
    assert "From what you follow" in html and "From what you follow" in text
    _, _, html2 = digest.build_digest(brief, "2026-06-09", "http://u", followed=[])
    assert "From what you follow" not in html2


def test_followed_digest_items_empty_when_no_follows(tmp_path):
    c = connect(str(tmp_path / "fd2.db")); init_db(c)
    _seed(c, n=5)
    assert digest.followed_digest_items(c, 1, exclude_ids=[], limit=3) == []
