"""Moodle → MEL outcome pull (mocked Web Services)."""
from unittest.mock import patch

import pytest
from django.contrib.auth import get_user_model

from apps.mel.feedback.models import CourseEvaluation
from apps.mel.indicators.models import DataPoint
from apps.mel.indicators.seeders.indicators_seeder import seed_indicators

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


class _FakeMoodleClient:
    """Stands in for MoodleClient with canned WS responses."""

    configured = True

    def __init__(self, *args, **kwargs):
        pass

    def call(self, wsfunction, **params):
        if wsfunction == "core_course_get_courses":
            return [
                {"id": 1, "shortname": "site"},  # site "course" — must be skipped
                {"id": 2, "idnumber": "AG101", "shortname": "ag101"},
            ]
        if wsfunction == "core_enrol_get_enrolled_users":
            return [{"id": 10, "email": "learner@demo.local"}]
        if wsfunction == "core_completion_get_course_completion_status":
            return {"completionstatus": {"completed": True}}
        if wsfunction == "mod_feedback_get_feedbacks_by_courses":
            return {"feedbacks": [{"id": 5, "name": "Course feedback"}]}
        if wsfunction == "mod_feedback_get_analysis":
            return {"completedcount": 7}
        if wsfunction == "mod_quiz_get_quizzes_by_courses":
            return {"quizzes": [{"id": 8, "name": "Unit quiz"}]}
        if wsfunction == "mod_quiz_get_user_attempts":
            return {"attempts": [{"id": 100, "state": "finished"}]}
        if wsfunction == "gradereport_user_get_grade_items":
            return {
                "usergrades": [
                    {"gradeitems": [
                        {"id": 55, "graderaw": 85.0},   # recorded → counted
                        {"id": 56, "graderaw": None},   # ungraded → skipped
                    ]}
                ]
            }
        return None


def _run_pull():
    from apps.mel.tracking.tasks import pull_moodle_outcomes

    with patch("apps.mel.tracking.moodle_client.MoodleClient", _FakeMoodleClient):
        return pull_moodle_outcomes()


def test_pull_records_datapoints_and_evaluation():
    seed_indicators()
    learner = User.objects.create_user(email="learner@demo.local", password="x")

    result = _run_pull()
    assert result["configured"] is True
    assert result["courses"] == 1  # site course (id=1) skipped

    # Enrolment + completion data points landed for the real course only.
    assert DataPoint.objects.filter(
        indicator__code="moodle-enrolments",
        source_module="moodle",
        source_object_id="enrol:10:2",
    ).exists()
    assert DataPoint.objects.filter(
        indicator__code="moodle-courses-completed",
        source_module="moodle",
        source_object_id="completion:10:2",
    ).exists()

    # Finished quiz attempt + recorded grade item landed.
    assert result["quiz_submissions"] == 1
    assert result["grades"] == 1
    assert DataPoint.objects.filter(
        indicator__code="moodle-quiz-submissions",
        source_module="moodle",
        source_object_id="quizattempt:100",
    ).exists()
    assert DataPoint.objects.filter(
        indicator__code="moodle-grades-recorded",
        source_module="moodle",
        source_object_id="grade:10:55",
    ).exists()
    # The ungraded item (graderaw=None) is not counted.
    assert not DataPoint.objects.filter(source_object_id="grade:10:56").exists()

    # Feedback mirrored into CourseEvaluation.
    ev = CourseEvaluation.objects.get(source_system="moodle", course_slug="AG101")
    assert ev.response_count == 7

    # lms_ref stamped from the matching email.
    learner.refresh_from_db()
    assert learner.lms_ref == 10


def test_pull_is_idempotent():
    seed_indicators()
    User.objects.create_user(email="learner@demo.local", password="x")

    _run_pull()
    before = DataPoint.objects.filter(source_module="moodle").count()
    _run_pull()
    after = DataPoint.objects.filter(source_module="moodle").count()
    assert before == after  # re-running creates no duplicate data points


def test_pull_noop_when_unconfigured():
    from apps.mel.tracking.tasks import pull_moodle_outcomes

    class _Unconfigured(_FakeMoodleClient):
        configured = False

    with patch("apps.mel.tracking.moodle_client.MoodleClient", _Unconfigured):
        result = pull_moodle_outcomes()
    assert result == {"configured": False}
