"""Phase 4.5 — cross-module participant timeline."""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model

from apps.core.permissions.roles import UserRole
from apps.mel.tracking.models import (
    BaselineDimension,
    BaselineRecord,
    Participant,
    ParticipantPersona,
    TrackingLifecycle,
    TrackingMilestone,
    TrackingRecord,
)
from apps.mel.tracking.services import (
    capture_baseline,
    ensure_participant,
    record_event,
)

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


@pytest.fixture
def user():
    return User.objects.create_user(
        email="participant@example.com",
        password="x",
        role=UserRole.LEARNER,
    )


def test_ensure_participant_is_idempotent_and_adds_persona(user):
    p1 = ensure_participant(user, persona=ParticipantPersona.LEARNER)
    p2 = ensure_participant(user, persona=ParticipantPersona.SCHOLAR)
    assert p1.pk == p2.pk
    p1.refresh_from_db()
    assert ParticipantPersona.LEARNER in p1.personas
    assert ParticipantPersona.SCHOLAR in p1.personas
    # First persona becomes primary; subsequent persona doesn't override.
    assert p1.primary_persona == ParticipantPersona.LEARNER


def test_ensure_participant_returns_none_when_user_missing():
    assert ensure_participant(None) is None


def test_record_event_creates_record_and_milestone_idempotently(user):
    m1 = record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=42,
        payload={"call_slug": "phd-2026"},
        label="RIMS award",
    )
    m2 = record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=42,
        payload={"call_slug": "phd-2026"},
    )
    assert m1.pk == m2.pk
    assert TrackingRecord.objects.filter(
        participant__user=user, source_module=TrackingRecord.Module.RIMS
    ).count() == 1
    record = m1.record
    assert record.last_event_at is not None
    assert record.status == TrackingLifecycle.ACTIVE


def test_record_event_completes_record_on_terminal_event(user):
    record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=1,
    )
    record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="scholar_graduated",
        source_id=99,
    )
    record = TrackingRecord.objects.get(participant__user=user, source_module="rims")
    assert record.status == TrackingLifecycle.COMPLETED
    assert record.completed_at is not None


def test_record_event_separates_modules(user):
    record_event(
        user=user, source_module=TrackingRecord.Module.RIMS, event_type="award_created", source_id=1
    )
    record_event(
        user=user, source_module=TrackingRecord.Module.REP, event_type="enrolment_created", source_id=2
    )
    assert TrackingRecord.objects.filter(participant__user=user).count() == 2
    p = Participant.objects.get(user=user)
    # Two separate records, but personas merge on the participant.
    assert ParticipantPersona.SCHOLAR in p.personas
    assert ParticipantPersona.LEARNER in p.personas


def test_capture_baseline_writes_dimension(user):
    bl = capture_baseline(
        user=user,
        source_module=TrackingRecord.Module.SMEHUB,
        dimension=BaselineDimension.BUSINESS_REVENUE,
        value_numeric=10_000,
        unit="USD",
        note="SP1 baseline",
    )
    assert bl is not None
    assert BaselineRecord.objects.filter(participant__user=user).count() == 1
    assert bl.value_numeric == 10_000


def test_record_event_swallows_failures_silently(monkeypatch, user):
    """Failures inside record_event must never propagate — upstream
    transactions can't tolerate a MEL-side bug rolling them back."""
    def boom(*args, **kwargs):
        raise RuntimeError("simulated MEL failure")

    monkeypatch.setattr(TrackingMilestone.objects, "get_or_create", boom)
    # Should not raise.
    result = record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=7,
    )
    assert result is None


def test_dashboard_view_renders_for_mel_officer(client, user):
    user.role = UserRole.MEL_OFFICER
    user.save(update_fields=["role"])
    client.force_login(user)
    record_event(
        user=user,
        source_module=TrackingRecord.Module.REP,
        event_type="enrolment_created",
        source_id=1,
    )
    response = client.get("/mel/events/dashboard/", HTTP_HOST="localhost")
    assert response.status_code == 200
    assert b"Cross-module impact" in response.content


def test_participant_record_view_renders(client, user):
    user.role = UserRole.MEL_OFFICER
    user.save(update_fields=["role"])
    client.force_login(user)
    record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=11,
        label="Demo award",
    )
    p = Participant.objects.get(user=user)
    response = client.get(f"/mel/events/participants/{p.pk}/", HTTP_HOST="localhost")
    assert response.status_code == 200
    assert b"Demo award" in response.content


def test_timeline_fragment_filters_by_module(client, user):
    user.role = UserRole.MEL_OFFICER
    user.save(update_fields=["role"])
    client.force_login(user)
    record_event(
        user=user,
        source_module=TrackingRecord.Module.RIMS,
        event_type="award_created",
        source_id=20,
        label="RIMS award",
    )
    record_event(
        user=user,
        source_module=TrackingRecord.Module.REP,
        event_type="course_completed",
        source_id=21,
        label="Course done",
    )
    p = Participant.objects.get(user=user)
    response = client.get(
        f"/mel/events/participants/{p.pk}/timeline/?module=rep",
        HTTP_HOST="localhost",
    )
    assert response.status_code == 200
    assert b"Course done" in response.content
    assert b"RIMS award" not in response.content
