"""Pagination on tracking detail/directory surfaces.

These views previously hard-capped collections with a slice (``[:100]`` /
``[:200]``) or rendered them fully unbounded, so rows past the cap were
invisible. They now use ``paginate_qs`` + the shared pager. Each test creates
more than one page of rows and asserts paging, out-of-range clamping, and that
sibling pagers on the same page use distinct query params.
"""
from __future__ import annotations

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

from datetime import date

from apps.core.permissions.roles import UserRole
from apps.mel.indicators.models import LogFrame, LogFrameLevel, LogFrameRow
from apps.mel.tracking.models import (
    Activity,
    OutputDeliverable,
    Participant,
    SMEHubBeneficiaryFeedback,
    TrackingRecord,
)
from apps.mel.tracking.services import ensure_participant, record_event
from apps.mel.tracking.smehub_employment import record_employment_snapshot

from apps.mel.tracking.tests.test_phase8_employment_snapshot import _build_record  # noqa: F401

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


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


# ---------------------------------------------------------------------------
# ParticipantDirectoryView — P0 (was hard-capped at 100, no pager)
# ---------------------------------------------------------------------------

def test_participant_directory_paginates_at_50(client, officer):
    for i in range(60):
        u = User.objects.create_user(
            email=f"pdir-{i}@example.com", password="x", role=UserRole.LEARNER,
        )
        ensure_participant(u)
    client.force_login(officer)
    url = reverse("mel_tracking:participant_directory")

    page1 = client.get(url, HTTP_HOST="localhost")
    assert page1.status_code == 200
    page = page1.context["participants"]
    assert page.paginator.num_pages == 2
    assert len(page.object_list) == 50

    page2 = client.get(url, {"page": 2}, HTTP_HOST="localhost")
    assert page2.status_code == 200
    assert len(page2.context["participants"].object_list) == 10


def test_participant_directory_out_of_range_clamps(client, officer):
    for i in range(60):
        u = User.objects.create_user(
            email=f"pdir2-{i}@example.com", password="x", role=UserRole.LEARNER,
        )
        ensure_participant(u)
    client.force_login(officer)
    url = reverse("mel_tracking:participant_directory")
    # get_page semantics: out-of-range never 404s, it clamps to the last page.
    resp = client.get(url, {"page": 999}, HTTP_HOST="localhost")
    assert resp.status_code == 200
    assert resp.context["participants"].number == 2


# ---------------------------------------------------------------------------
# SMEHubTrackingDetailView — P1 (two unbounded tables, distinct params)
# ---------------------------------------------------------------------------

@pytest.fixture
def admin():
    return User.objects.create_user(
        email="pager-admin@example.com", password="x",
        is_staff=True, is_superuser=True, role=UserRole.SYSTEM_ADMIN,
    )


def test_smehub_detail_surveys_and_snapshots_paginate_independently(client, admin, officer):
    record = _build_record(admin, fte=2, pte=3, suffix="pager")
    SMEHubBeneficiaryFeedback.objects.bulk_create(
        SMEHubBeneficiaryFeedback(
            tracking_record=record,
            stage=SMEHubBeneficiaryFeedback.Stage.POST_INCUBATION,
            trigger_event=f"evt:{i}",
        )
        for i in range(30)
    )
    for i in range(30):
        record_employment_snapshot(record, fte_count=i + 1, pte_count=1, by_user=officer)

    client.force_login(officer)
    url = reverse("mel_tracking:smehub_tracking_detail", kwargs={"pk": record.pk})

    # Page the surveys; snapshots must stay on page 1 (distinct params).
    resp = client.get(url, {"survey_page": 2}, HTTP_HOST="localhost")
    assert resp.status_code == 200
    assert resp.context["surveys"].number == 2
    assert resp.context["surveys"].paginator.per_page == 25
    assert resp.context["employment_snapshots"].number == 1

    # And the reverse — snapshot_page must not disturb the survey pager.
    resp = client.get(url, {"snapshot_page": 2}, HTTP_HOST="localhost")
    assert resp.context["employment_snapshots"].number == 2
    assert resp.context["surveys"].number == 1


# ---------------------------------------------------------------------------
# ParticipantRecordView + TimelineFragmentView — P1 (was [:200])
# ---------------------------------------------------------------------------

def _make_timeline(user, module, n):
    for i in range(n):
        record_event(
            user=user,
            source_module=module,
            event_type="award_created" if module == TrackingRecord.Module.RIMS else "course_completed",
            source_id=i,
            label=f"{module}-evt-{i}",
        )


def test_participant_record_timeline_paginates(client, officer):
    learner = User.objects.create_user(
        email="tl-learner@example.com", password="x", role=UserRole.LEARNER,
    )
    _make_timeline(learner, TrackingRecord.Module.RIMS, 60)
    participant = Participant.objects.get(user=learner)

    client.force_login(officer)
    url = reverse("mel_tracking:participant_record", kwargs={"pk": participant.pk})
    resp = client.get(url, HTTP_HOST="localhost")
    assert resp.status_code == 200
    page = resp.context["milestones"]
    assert page.paginator.num_pages == 2
    assert len(page.object_list) == 50


def test_timeline_fragment_carries_module_across_pages(client, officer):
    learner = User.objects.create_user(
        email="tl-frag@example.com", password="x", role=UserRole.LEARNER,
    )
    _make_timeline(learner, TrackingRecord.Module.RIMS, 60)
    _make_timeline(learner, TrackingRecord.Module.REP, 5)
    participant = Participant.objects.get(user=learner)

    client.force_login(officer)
    url = reverse("mel_tracking:participant_timeline_fragment", kwargs={"pk": participant.pk})
    # Filter by module + ask for page 2: only RIMS rows, second page.
    resp = client.get(url, {"module": "rims", "tl_page": 2}, HTTP_HOST="localhost")
    assert resp.status_code == 200
    page = resp.context["milestones"]
    assert page.number == 2
    assert resp.context["active_module"] == "rims"
    # The pager preserves ?module= so a page click keeps the filter.
    assert b'module=rims' in resp.content


# ---------------------------------------------------------------------------
# P2 — OutputDeliverableDetailView linked activities (was [:50])
# ---------------------------------------------------------------------------

def test_output_detail_linked_activities_paginate(client, officer):
    lf = LogFrame.objects.create(name="LF-OUT", slug="lf-out-pager")
    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_row = LogFrameRow.objects.create(
        logframe=lf, level=LogFrameLevel.OUTPUT, title="P", parent=outcome
    )
    deliverable = OutputDeliverable.objects.create(
        logframe_row=output_row, title="Reports", target_quantity=10, unit="reports",
    )
    for i in range(30):
        act_row = LogFrameRow.objects.create(
            logframe=lf, level=LogFrameLevel.ACTIVITY, title=f"A{i}", parent=output_row
        )
        Activity.objects.create(
            logframe_row=act_row,
            name=f"Activity {i}",
            scheduled_start=date(2026, 1, 1),
            scheduled_end=date(2026, 2, 1),
        )

    client.force_login(officer)
    url = reverse("mel_tracking:output_detail", kwargs={"pk": deliverable.pk})

    resp = client.get(url, HTTP_HOST="localhost")
    assert resp.status_code == 200
    page = resp.context["linked_activities"]
    assert page.paginator.count == 30
    assert len(page.object_list) == 25

    resp2 = client.get(url, {"act_page": 2}, HTTP_HOST="localhost")
    assert len(resp2.context["linked_activities"].object_list) == 5
