from datetime import timedelta

import pytest
from django.urls import reverse

from apps.ads.models import Publicite
from apps.contact.models import Message, Subscriber
from apps.core.html import sanitize_html
from apps.editorial.models import Article, ArticleImage, Status
from apps.engagement.models import Comment, Reaction
from apps.medialib.models import MediaAsset
from apps.medialib.services import create_asset

pytestmark = pytest.mark.django_db
UA = {"HTTP_USER_AGENT": "Mozilla/5.0"}


def test_sanitizer_keeps_editorial_html_and_strips_danger():
    html = sanitize_html(
        '<p onclick="x()">Texte <a href="javascript:alert(1)">lien</a></p><script>bad()</script>'
        '<iframe src="https://www.youtube.com/embed/abc"></iframe><iframe src="https://evil.example/x"></iframe>'
        '<figure><img src="/storage/a.jpg" alt="A"><figcaption>Légende</figcaption></figure>'
    )
    assert "onclick" not in html and "<script" not in html and "javascript:" not in html
    assert 'src="https://www.youtube.com/embed/abc"' in html
    assert "evil.example" not in html
    assert "<figcaption>Légende</figcaption>" in html


@pytest.mark.parametrize("name", ["home", "articles", "annonces", "services", "contact", "about", "legal", "privacy", "terms", "sitemap", "robots"])
def test_public_pages(client, name, article_factory):
    article_factory()
    assert client.get(reverse(f"public:{name}"), **UA).status_code == 200


def test_legacy_urls_are_preserved(client, article_factory, category):
    a = article_factory(titre="Réforme foncière")
    assert client.get(f"/article/{a.slug}", **UA).status_code == 200
    assert client.get(f"/categorie/{category.slug}").status_code == 200
    assert client.get("/search?q=foncière").status_code == 200
    assert client.get("/register/").status_code == 301


def test_view_counted_once_per_session_and_not_for_bots(client, article_factory):
    a = article_factory()
    url = reverse("public:article", args=[a.slug])
    client.get(url, **UA)
    client.get(url, **UA)
    client.get(url, HTTP_USER_AGENT="Googlebot/2.1")
    a.refresh_from_db()
    assert a.vues == 1


def test_next_article_partial(client, article_factory):
    older = article_factory(titre="Plus ancien")
    newer = article_factory(titre="Plus récent")
    newer.publie_le = older.publie_le + timedelta(hours=1)
    newer.save()
    html = client.get(reverse("public:article-next", args=[newer.pk]), **UA).content.decode()
    assert "Plus ancien" in html


def test_comment_is_moderated(client, article_factory):
    a = article_factory()
    client.post(reverse("public:comment-create", args=[a.pk]), {"nom": "Ami", "email": "a@b.bj", "contenu": "Très bon article"})
    c = Comment.objects.get()
    assert c.statut == Comment.Status.EN_ATTENTE
    assert "Très bon article" not in client.get(a.get_absolute_url(), **UA).content.decode()
    c.statut = Comment.Status.APPROUVE
    c.save()
    assert "Très bon article" in client.get(a.get_absolute_url(), **UA).content.decode()


def test_comment_honeypot_blocks_bots(client, article_factory):
    a = article_factory()
    client.post(reverse("public:comment-create", args=[a.pk]), {"nom": "Bot", "email": "b@b.bj", "contenu": "Spam spam", "website": "http://spam"})
    assert not Comment.objects.exists()


def test_reaction_toggle(client, article_factory):
    a = article_factory()
    url = reverse("public:react", args=[a.pk])
    assert client.post(url).json()["hasLiked"] is True
    assert client.post(url).json()["hasLiked"] is False
    assert Reaction.objects.count() == 0


def test_contact_and_newsletter(client):
    client.post(reverse("public:contact"), {"nom": "A", "email": "a@b.bj", "sujet": "Pub", "message": "Bonjour"})
    assert Message.objects.count() == 1
    r = client.post(reverse("public:subscribe"), {"email": "LECTEUR@b.bj"}, HTTP_ACCEPT="application/json")
    assert r.json()["success"] and Subscriber.objects.get().email == "lecteur@b.bj"


def test_media_upload_variants_and_protected_delete(client, editeur, article_factory, image_file):
    client.force_login(editeur)
    r = client.post(reverse("medialib:upload"), {"file": image_file(), "collection": "articles"})
    assert r.status_code == 201
    asset = MediaAsset.objects.get(pk=r.json()["id"])
    assert asset.is_image and asset.largeur == 1400
    assert set(asset.variantes) == {"sm", "md", "lg"}

    a = article_factory(image_principale=asset)
    client.post(reverse("medialib:delete", args=[asset.pk]))
    assert MediaAsset.objects.filter(pk=asset.pk).exists()  # utilisé → suppression refusée
    a.image_principale = None
    a.save()
    client.post(reverse("medialib:delete", args=[asset.pk]))
    assert not MediaAsset.objects.filter(pk=asset.pk).exists()


def test_upload_rejects_executables(client, editeur):
    from django.core.files.uploadedfile import SimpleUploadedFile

    client.force_login(editeur)
    r = client.post(reverse("medialib:upload"), {"file": SimpleUploadedFile("x.php", b"<?php ?>")})
    assert r.status_code == 422


def test_gallery_added_from_library(client, editeur, category, image_file):
    client.force_login(editeur)
    assets = [create_asset(image_file(f"g{i}.jpg"), user=editeur) for i in range(2)]
    client.post(reverse("editorial:article-create"), {
        "titre": "Avec galerie", "contenu": "<p>x</p>", "categorie": category.pk,
        "gallery_media": ",".join(str(a.pk) for a in assets), "submit_action": "publish",
    })
    art = Article.objects.get(titre="Avec galerie")
    assert art.is_published and ArticleImage.objects.filter(article=art).count() == 2


def test_ad_click_is_counted(client, editeur, image_file):
    ad = Publicite.objects.create(titre="Pub", image=create_asset(image_file()), lien="https://example.com")
    r = client.get(reverse("public:ad-click", args=[ad.pk]))
    assert r.status_code == 302 and r["Location"] == "https://example.com"
    ad.refresh_from_db()
    assert ad.clics == 1


def test_scheduled_article_not_public(client, article_factory):
    from django.utils import timezone

    a = article_factory(statut=Status.PUBLIE, publie_le=timezone.now() + timedelta(hours=2))
    assert client.get(a.get_absolute_url(), **UA).status_code == 404
    assert a not in Article.objects.published()
