# Docs: /guides/send-newsletters-python
import unittest
import json
from unittest.mock import patch, MagicMock
from newsletter import campaign, MockMailKite, SUBSCRIBERS, live_config, management_token
from mailkite import MailKite


class NewsletterTests(unittest.TestCase):
    config = {"from": "notes@example.com", "replyTo": "reply@example.com", "footerAddress": "Mock"}

    def test_exclusion_dedupe_schedule_cancel(self):
        mk = MockMailKite()
        result = campaign(mk, SUBSCRIBERS, self.config, "schedule-cancel")
        self.assertEqual(result["audience"], ["ada@example.com"])
        self.assertEqual(result["accepted"]["audienceCount"], 1)
        self.assertEqual(result["accepted"]["status"], "scheduled")
        self.assertEqual(result["broadcast"]["status"], "draft")
        self.assertIsNone(result["broadcast"]["scheduled_at"])
        self.assertEqual(sum(path == "/api/contacts" for _, path, _ in mk.calls), 1)

    def test_draft_and_existing_unsubscribe(self):
        mk = MockMailKite()
        mk.contacts["past@example.com"] = {"id": "ctr_past", "email": "past@example.com", "status": "unsubscribed", "subscribed": 0}
        result = campaign(mk, SUBSCRIBERS + [{"email": "past@example.com", "consent": True}], self.config)
        self.assertEqual(result["audience"], ["ada@example.com"])
        self.assertFalse(any(path.endswith("/send") for _, path, _ in mk.calls))
        self.assertEqual(mk.contacts["past@example.com"]["subscribed"], 0)

    def test_negative_consent_wins(self):
        with self.assertRaisesRegex(ValueError, "No eligible"):
            campaign(MockMailKite(), [{"email": "a@example.com", "consent": False}, {"email": "A@example.com", "consent": True}], self.config)

    def test_live_guard(self):
        with self.assertRaisesRegex(ValueError, "Missing"):
            live_config({})

    def test_optout_after_draft(self):
        mk = MockMailKite()
        result = campaign(mk, SUBSCRIBERS, self.config)
        mk.addSuppression({"email": "ada@example.com", "reason": "unsubscribe"})
        with self.assertRaisesRegex(ValueError, "precheck_failed"):
            mk.sendBroadcast(result["broadcast"]["id"], {"scheduledAt": "2099-01-01T00:00:00+00:00"})
        self.assertEqual(mk.getBroadcast(result["broadcast"]["id"])["status"], "draft")
        self.assertEqual(mk.contacts["ada@example.com"]["status"], "unsubscribed")

    def test_sdk_login_and_management_auth(self):
        response = MagicMock()
        response.__enter__.return_value = response
        response.read.side_effect = [b'{"token":"mock-session","user":{"emailVerified":true}}', b'{"id":"lst_wire"}']
        with patch("urllib.request.urlopen", return_value=response) as mock:
            token = management_token({"MAILKITE_ACCOUNT_EMAIL": "test-fixture@example.com", "MAILKITE_ACCOUNT_PASSWORD": "local-fixture-only"})
            MailKite(accessToken=token).createList({"name": "Mock"})
        login_req = mock.call_args_list[0].args[0]
        list_req = mock.call_args_list[1].args[0]
        self.assertEqual(json.loads(login_req.data), {"email": "test-fixture@example.com", "password": "local-fixture-only"})
        self.assertEqual(list_req.full_url, "https://api.mailkite.dev/api/lists")
        self.assertEqual(list_req.get_header("Authorization"), "Bearer mock-session")


if __name__ == "__main__":
    unittest.main()
