from datetime import date
from types import SimpleNamespace
from unittest.mock import Mock, patch

from django.test import TestCase
from django.urls import reverse

from .newsletter import (
    extract_token,
    generate_one_click_token,
    build_weekly_news_body_html,
    render_placeholders,
    render_newsletter_html,
    verify_one_click_token,
)
from .models import ContactFormSubmission, SystemNotificationMailSettings
from .security import decrypt_text, encrypt_text


class NewsletterTemplateTests(TestCase):
    def test_render_placeholders_replaces_known_keys(self):
        template = "confirm={{confirm_url}} email={{email}} token={{token}}"
        rendered = render_placeholders(
            template,
            {
                "confirm_url": "https://example.com/confirm?token=abc",
                "email": "demo@example.com",
                "token": "abc",
            },
        )

        self.assertEqual(
            rendered,
            "confirm=https://example.com/confirm?token=abc email=demo@example.com token=abc",
        )

    def test_extract_token_supports_top_level_and_nested_data(self):
        self.assertEqual(extract_token({"token": "t1"}), "t1")
        self.assertEqual(extract_token({"data": {"unsubscribe_token": "t2"}}), "t2")
        self.assertEqual(extract_token({}), "")

    def test_encrypt_decrypt_roundtrip(self):
        secret = "mailrelay-secret"
        encrypted = encrypt_text(secret)
        self.assertTrue(encrypted.startswith("enc1:"))
        self.assertNotEqual(encrypted, secret)
        self.assertEqual(decrypt_text(encrypted), secret)

    def test_one_click_token_roundtrip(self):
        token = generate_one_click_token(
            subscriber_id="sub-1",
            list_id="list-1",
            site_id="site-1",
            campaign_id="cmp-1",
            secret="test-secret",
            ttl_seconds=60,
        )
        payload, error = verify_one_click_token(token, "test-secret")
        self.assertIsNone(error)
        self.assertEqual(payload["subscriber_id"], "sub-1")
        self.assertEqual(payload["list_id"], "list-1")

    def test_one_click_token_invalid_signature(self):
        token = generate_one_click_token(
            subscriber_id="sub-1",
            list_id="list-1",
            site_id="site-1",
            campaign_id="cmp-1",
            secret="test-secret",
            ttl_seconds=60,
        )
        payload, error = verify_one_click_token(token, "other-secret")
        self.assertIsNone(payload)
        self.assertEqual(error, "invalid")

    def test_render_newsletter_html_absolutizes_relative_links(self):
        rendered = render_newsletter_html(
            '<p><a href="/a">A</a><img src="/media/x.jpg"></p>',
            values={},
            site_base_url="https://news.example.com",
        )
        self.assertIn('href="https://news.example.com/a"', rendered)
        self.assertIn('src="https://news.example.com/media/x.jpg"', rendered)

    def test_newsletter_subscribe_uses_system_notification_template(self):
        notification_settings = SystemNotificationMailSettings.load()
        notification_settings.subscribe_subject_template = "Confirm {{email}}"
        notification_settings.subscribe_html_template = "<table><tr><td>New {{confirm_url}}</td></tr></table>"
        notification_settings.subscribe_text_template = "Text {{confirm_url}}"
        notification_settings.save()

        with (
            patch("base.views.MemberCenterClient") as client_cls,
            patch("base.views.send_subscribe_email") as send_email,
        ):
            client_cls.return_value.subscribe.return_value = SimpleNamespace(
                ok=True,
                data={"token": "abc123"},
            )

            response = self.client.post(
                reverse("newsletter_subscribe"),
                data={"email": "demo@example.com"},
            )

        self.assertEqual(response.status_code, 200)
        send_email.assert_called_once()
        kwargs = send_email.call_args.kwargs
        self.assertEqual(kwargs["subject"], "Confirm demo@example.com")
        self.assertIn("<table><tr><td>New http://testserver/newsletter/confirm/", kwargs["html_body"])

    def test_weekly_news_body_uses_fixed_size_cover_rendition(self):
        image = Mock()
        image.get_rendition.return_value = SimpleNamespace(url="/media/images/article.fill-596x333.jpg")
        image.file.url = "/media/images/article-original.jpg"
        article = SimpleNamespace(
            id=12,
            title="Weekly title",
            intro="Weekly intro",
            date=date(2026, 6, 30),
            url="/news/weekly-title/",
            cover_image_id=34,
            cover_image=image,
        )
        item_template_html = (
            '<a href="{{article_url}}">{{article_title}}</a>'
            '<img src="{{article_cover_url}}" alt="{{article_title}}">'
            "<p>{{article_intro}}</p>"
        )

        with patch("home.models.ArticlePage") as article_page:
            article_page.objects.live.return_value.filter.return_value = [article]
            rendered = build_weekly_news_body_html(
                article_page_ids=[article.id],
                item_template_html=item_template_html,
                site_base_url="https://debutai.net",
            )

        image.get_rendition.assert_called_once_with("fill-596x333")
        self.assertIn('href="https://debutai.net/news/weekly-title/"', rendered)
        self.assertIn('src="https://debutai.net/media/images/article.fill-596x333.jpg"', rendered)
        self.assertIn("Weekly intro", rendered)

    def test_contact_form_submit_saves_submission(self):
        response = self.client.post(
            reverse("contact_form_submit"),
            data={
                "name": "Tester",
                "contact": "tester@example.com",
                "email": "tester@example.com",
                "category": "other",
                "message": "hello",
                "source_page": "/",
            },
            HTTP_X_REQUESTED_WITH="XMLHttpRequest",
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(ContactFormSubmission.objects.count(), 1)
        submission = ContactFormSubmission.objects.first()
        self.assertEqual(submission.name, "Tester")
        self.assertEqual(submission.email, "tester@example.com")
        self.assertEqual(submission.category, "other")

    def test_contact_form_submit_rejects_invalid_email(self):
        response = self.client.post(
            reverse("contact_form_submit"),
            data={
                "name": "Tester",
                "contact": "123",
                "email": "not-an-email",
                "category": "other",
                "message": "hello",
                "source_page": "/",
            },
            HTTP_X_REQUESTED_WITH="XMLHttpRequest",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(ContactFormSubmission.objects.count(), 0)
