"""SRS QA fixes 2026-07-12.

* Fix 1 — event detail + .ics honour visibility (restricted events 404 for
  unauthorised viewers, never leaking the private virtual_url).
* Fix 2 — mentorship match_score is computed and stored at request time.
* Fix 3 — post-event feedback capture records rating + attendance, and the
  post-event pulse aggregates it.
"""
from __future__ import annotations

from datetime import timedelta

import pytest
from django.urls import reverse
from django.utils import timezone

from apps.alumni.engagement.models import (
    AlumniEvent,
    EventRegistration,
    EventVisibility,
    GroupMembership,
    MentorshipPairing,
    MentorshipStatus,
    NetworkGroup,
)
from apps.alumni.profiles.models import AlumniProfile


# ---------------------------------------------------------------------------
# Fix 1 — visibility gate on detail + ics
# ---------------------------------------------------------------------------


def _future_event(title, visibility):
    start = timezone.now() + timedelta(days=5)
    return AlumniEvent.objects.create(
        title=title,
        start_at=start,
        end_at=start + timedelta(hours=2),
        visibility=visibility,
        virtual_url="https://zoom.example/secret-room",
    )


@pytest.mark.django_db
def test_detail_anonymous_blocked_from_alumni_only(client):
    event = _future_event("Private Roundtable", EventVisibility.ALUMNI_ONLY)
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 404


@pytest.mark.django_db
def test_detail_anonymous_allowed_public(client):
    event = _future_event("Open Demo Day", EventVisibility.PUBLIC)
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 200


@pytest.mark.django_db
def test_detail_group_event_hidden_from_non_member(client, alumni_user):
    event = _future_event("Chapter Only", EventVisibility.GROUP)
    group = NetworkGroup.objects.create(title="Secret Chapter")
    event.groups.add(group)
    client.force_login(alumni_user)  # authenticated, but not a member
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 404


@pytest.mark.django_db
def test_detail_group_event_visible_to_member(client, alumni_user):
    event = _future_event("Chapter Only", EventVisibility.GROUP)
    group = NetworkGroup.objects.create(title="Secret Chapter")
    event.groups.add(group)
    GroupMembership.objects.create(user=alumni_user, group=group)
    client.force_login(alumni_user)
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 200


@pytest.mark.django_db
def test_ics_gated_by_visibility(client):
    """The .ics embeds the private virtual_url — it must 404 for the public."""
    event = _future_event("Private Roundtable", EventVisibility.ALUMNI_ONLY)
    resp = client.get(reverse("alumni_engagement:event_ics", args=[event.slug]))
    assert resp.status_code == 404


@pytest.mark.django_db
def test_ics_public_downloads(client):
    event = _future_event("Open Demo Day", EventVisibility.PUBLIC)
    resp = client.get(reverse("alumni_engagement:event_ics", args=[event.slug]))
    assert resp.status_code == 200
    assert resp["Content-Type"].startswith("text/calendar")


# ---------------------------------------------------------------------------
# Fix 2 — match_score stored at request time
# ---------------------------------------------------------------------------


@pytest.mark.django_db
def test_request_stores_nonzero_match_score(alumni_user, scholar_user):
    from apps.alumni.engagement.services import request_mentorship

    # Mentee (scholar) needs an alumni profile with overlapping expertise.
    AlumniProfile.objects.create(
        user=scholar_user, expertise_tags=["agronomy", "policy"]
    )
    mentor_profile = AlumniProfile.objects.create(
        user=alumni_user,
        mentor_accepting=True,
        expertise_tags=["agronomy", "climate"],
    )
    pairing = request_mentorship(
        scholar_user, mentor_profile, topic="Research methods"
    )
    assert pairing.match_score > 0.0
    pairing.refresh_from_db()
    assert pairing.match_score > 0.0


@pytest.mark.django_db
def test_request_without_mentee_profile_defaults_gracefully(alumni_user, scholar_user):
    """A mentee with no alumni profile / no overlap still gets a stored score
    (0.0) without raising."""
    from apps.alumni.engagement.services import request_mentorship

    mentor_profile = AlumniProfile.objects.create(
        user=alumni_user, mentor_accepting=True, expertise_tags=["tax"]
    )
    pairing = request_mentorship(scholar_user, mentor_profile, topic="x")
    assert pairing.match_score == 0.0  # no overlap, but stored, no exception


# ---------------------------------------------------------------------------
# Fix 3 — post-event feedback capture + pulse aggregation
# ---------------------------------------------------------------------------


def _past_event(organiser=None):
    start = timezone.now() - timedelta(days=3)
    return AlumniEvent.objects.create(
        title="Wrapped Workshop",
        start_at=start,
        end_at=start + timedelta(hours=2),
        visibility=EventVisibility.PUBLIC,
        organiser=organiser,
    )


@pytest.mark.django_db
def test_feedback_post_records_rating_and_attendance(client, alumni_user):
    event = _past_event()
    EventRegistration.objects.create(user=alumni_user, event=event)
    client.force_login(alumni_user)
    resp = client.post(
        reverse("alumni_engagement:event_feedback", args=[event.slug]),
        {"rating": "4", "feedback_text": "Great session"},
    )
    assert resp.status_code == 302
    reg = EventRegistration.objects.get(user=alumni_user, event=event)
    assert reg.feedback_rating == 4
    assert reg.feedback_text == "Great session"
    assert reg.attended is True


@pytest.mark.django_db
def test_feedback_rejected_before_event_ends(client, alumni_user):
    start = timezone.now() + timedelta(days=2)
    event = AlumniEvent.objects.create(
        title="Upcoming",
        start_at=start,
        end_at=start + timedelta(hours=2),
        visibility=EventVisibility.PUBLIC,
    )
    EventRegistration.objects.create(user=alumni_user, event=event)
    client.force_login(alumni_user)
    resp = client.post(
        reverse("alumni_engagement:event_feedback", args=[event.slug]),
        {"rating": "5"},
    )
    assert resp.status_code == 302
    reg = EventRegistration.objects.get(user=alumni_user, event=event)
    assert reg.feedback_rating is None  # not recorded pre-event


@pytest.mark.django_db
def test_pulse_aggregates_feedback(client, alumni_user, scholar_user):
    event = _past_event()
    r1 = EventRegistration.objects.create(user=alumni_user, event=event)
    r2 = EventRegistration.objects.create(user=scholar_user, event=event)
    for reg, rating in ((r1, 4), (r2, 2)):
        reg.feedback_rating = rating
        reg.attended = True
        reg.save(update_fields=["feedback_rating", "attended"])
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 200
    assert resp.context["feedback_count"] == 2
    assert resp.context["feedback_avg"] == 3.0
    assert resp.context["attended_count"] == 2


@pytest.mark.django_db
def test_pulse_empty_state_when_no_feedback(client):
    event = _past_event()
    resp = client.get(
        reverse("alumni_engagement:event_detail", args=[event.slug])
    )
    assert resp.status_code == 200
    assert resp.context["feedback_count"] == 0
    assert b"No post-event feedback yet" in resp.content


@pytest.mark.django_db
def test_attendance_roster_gated_to_organiser_or_staff(client, alumni_user, scholar_user):
    event = _past_event(organiser=alumni_user)
    EventRegistration.objects.create(user=scholar_user, event=event)
    url = reverse("alumni_engagement:event_attendance", args=[event.slug])

    # Non-organiser, non-staff → 404
    client.force_login(scholar_user)
    assert client.get(url).status_code == 404

    # Organiser → 200 and can mark attendance via bulk POST
    client.force_login(alumni_user)
    assert client.get(url).status_code == 200
    reg = EventRegistration.objects.get(user=scholar_user, event=event)
    resp = client.post(url, {"attended": [str(reg.pk)]})
    assert resp.status_code == 302
    reg.refresh_from_db()
    assert reg.attended is True
