"""G2 — QR-code attendance & registration."""
from __future__ import annotations

from datetime import date, timedelta

import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils import timezone

from apps.core.permissions.roles import UserRole
from apps.mel.indicators.models import LogFrame, LogFrameLevel, LogFrameRow
from apps.mel.tracking.models import (
    Activity,
    ActivityAttendanceChannel,
    ActivityStatus,
    BeneficiaryAttendance,
)
from apps.mel.tracking.services import (
    AttendanceChannelClosedError,
    attendance_csv_rows,
    ensure_attendance_channel,
    record_attendance,
)

User = get_user_model()
pytestmark = pytest.mark.django_db


@pytest.fixture
def officer():
    return User.objects.create_user(
        email="g2-off@example.com", password="x", role=UserRole.MEL_OFFICER,
    )


@pytest.fixture
def activity():
    lf = LogFrame.objects.create(name="G2 LF", slug="g2-lf")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="P", parent=outcome)
    act_row = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.ACTIVITY, title="A", parent=output)
    return Activity.objects.create(
        logframe_row=act_row,
        name="Field workshop",
        scheduled_start=date(2026, 5, 1),
        scheduled_end=date(2026, 5, 2),
        status=ActivityStatus.PLANNED,
    )


def test_ensure_attendance_channel_idempotent(officer, activity):
    a = ensure_attendance_channel(activity, created_by=officer)
    b = ensure_attendance_channel(activity, created_by=officer)
    assert a.pk == b.pk
    assert ActivityAttendanceChannel.objects.count() == 1
    assert len(a.token) >= 32


def test_record_attendance_creates_row(officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    att, created = record_attendance(
        channel,
        attendee_name="Jane Doe",
        attendee_email="jane@example.com",
    )
    assert created is True
    assert att.attendee_name == "Jane Doe"
    assert att.attendee_email == "jane@example.com"
    assert att.dedupe_key


def test_record_attendance_dedupes_same_attendee(officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    record_attendance(channel, attendee_email="bob@example.com")
    _, created = record_attendance(channel, attendee_email="BOB@example.com")
    assert created is False  # case-insensitive email collision
    assert BeneficiaryAttendance.objects.count() == 1


def test_record_attendance_rejects_closed_channel(officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    channel.is_active = False
    channel.save(update_fields=["is_active"])
    with pytest.raises(AttendanceChannelClosedError):
        record_attendance(channel, attendee_email="late@example.com")


def test_attendance_csv_rows_have_header(officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    record_attendance(channel, attendee_name="Alice", attendee_email="alice@example.com")
    record_attendance(channel, attendee_name="Bob", attendee_email="bob@example.com")
    rows = attendance_csv_rows(activity)
    assert rows[0][:6] == ["attended_at", "name", "email", "phone", "organisation", "notes"]
    assert len(rows) == 3  # header + 2


def test_public_attendance_view_get_returns_form(client, officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    url = reverse("mel_tracking:attendance_public", kwargs={"token": channel.token})
    response = client.get(url)
    assert response.status_code == 200
    assert b"Check in" in response.content


def test_public_attendance_view_post_records_attendance(client, officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    url = reverse("mel_tracking:attendance_public", kwargs={"token": channel.token})
    response = client.post(url, {
        "attendee_name": "Carol",
        "attendee_email": "carol@example.com",
        "attendee_phone": "",
        "organisation": "RUFORUM",
        "notes": "",
        "consent": "on",
    })
    assert response.status_code == 200
    assert BeneficiaryAttendance.objects.filter(attendee_name="Carol").exists()


def test_closed_window_returns_410(client, officer, activity):
    channel = ensure_attendance_channel(activity, created_by=officer)
    channel.closes_at = timezone.now() - timedelta(hours=1)
    channel.save(update_fields=["closes_at"])
    url = reverse("mel_tracking:attendance_public", kwargs={"token": channel.token})
    response = client.get(url)
    assert response.status_code == 410


def test_csv_export_view_serves_csv(client, officer, activity):
    ensure_attendance_channel(activity, created_by=officer)
    client.force_login(officer)
    url = reverse("mel_tracking:activity_attendance_csv", kwargs={"pk": activity.pk})
    response = client.get(url)
    assert response.status_code == 200
    assert response["Content-Type"].startswith("text/csv")
    assert b"attended_at,name,email" in response.content
