# (c) cavaliba.com - tests / sirene - one mail.send / sms.send event listing every recipient

from unittest.mock import MagicMock, patch

from django.test import TestCase

import app_home.cache as cache
from app_home.log import log2
from app_home.models import CavalibaConfiguration, CavalibaEvent
from app_sirene import mail as sirene_mail
from app_sirene.sms import task_send_sms

AAA = {"username": "alice", "perms": []}


class TestSireneSendRecipientsLogged(TestCase):
    def setUp(self):
        cache.clear()
        log2.reset(channel="celery", actor="alice")

    def tearDown(self):
        # configuration rows roll back with the test, the in-memory cache does not
        cache.clear()

    def _config(self, keyname, value):
        CavalibaConfiguration.objects.update_or_create(
            appname="sirene", keyname=keyname, defaults={"value": value}
        )
        cache.clear()

    def _event(self, action):
        return CavalibaEvent.objects.filter(action=action).order_by("-id").first()

    def _send_mail(self, dests):
        with patch("builtins.print"):
            return sirene_mail.sirene_send_mail("Subject", "body", "from@example.com", dests)

    def test_sms_lists_sent_and_failed_numbers(self):
        with patch("builtins.print"):
            count = task_send_sms(
                ["+33600000001", "not-a-number", "+33600000002"], "hello", aaa=AAA
            )
        self.assertEqual(count, 2)
        event = self._event("sms.send")
        self.assertEqual(
            (event.outcome, event.reason, event.level), ("failure", "partial", "WARNING")
        )
        self.assertEqual(event.extra, {
            "recipients": 3, "sent": ["+33600000001", "+33600000002"], "failed": ["not-a-number"],
        })  # fmt: skip

    def test_sms_all_sent(self):
        with patch("builtins.print"):
            task_send_sms(["+33600000001"], "hello", aaa=AAA)
        event = self._event("sms.send")
        self.assertEqual(event.outcome, "success")
        self.assertEqual(event.extra["sent"], ["+33600000001"])
        self.assertEqual(event.extra["failed"], [])

    def test_mail_stdout_lists_sent_and_invalid(self):
        ok = self._send_mail(["a@example.com", "not-an-email", "b@example.com"])
        self.assertTrue(ok)
        event = self._event("mail.send")
        self.assertEqual((event.outcome, event.level, event.count), ("success", "WARNING", 2))
        self.assertEqual(event.extra["sent"], ["a@example.com", "b@example.com"])
        self.assertEqual(event.extra["invalid"], ["not-an-email"])
        self.assertEqual(event.extra["failed"], [])
        self.assertEqual((event.extra["mode"], event.extra["recipients"]), ("stdout", 3))

    def test_mail_smtp_failed_batch_lists_its_addresses(self):
        self._config("EMAIL_MODE", "smtp")
        self._config("EMAIL_SMTP_BATCH", "1")
        with patch.object(sirene_mail, "smtp_batch", side_effect=[True, False]):
            ok = self._send_mail(["a@example.com", "b@example.com"])
        self.assertFalse(ok)
        event = self._event("mail.send")
        self.assertEqual((event.outcome, event.reason), ("failure", "partial"))
        self.assertEqual(event.extra["sent"], ["a@example.com"])
        self.assertEqual(event.extra["failed"], ["b@example.com"])

    def test_mail_unknown_mode(self):
        self._config("EMAIL_MODE", "pigeon")
        self.assertFalse(self._send_mail(["a@example.com"]))
        event = self._event("mail.send")
        self.assertEqual((event.reason, event.extra["failed"]), ("unknown_mode", ["a@example.com"]))

    def test_smtp_close_failure_still_counts_as_sent(self):
        connection = MagicMock()
        connection.close.side_effect = OSError("broken pipe")
        with patch.object(sirene_mail.mail, "get_connection", return_value=connection):
            self.assertTrue(sirene_mail.smtp_batch([MagicMock()]))
        warning = self._event("mail.smtp")
        self.assertEqual((warning.reason, warning.level), ("close_failed", "WARNING"))
