"""Unit tests for feedback services."""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model

from apps.mel.feedback.models import (
    FeedbackChannel,
    FeedbackChannelType,
    FeedbackSeverity,
    FeedbackStatus,
    FeedbackSubmission,
)
from apps.mel.feedback.services import (
    ingest_course_evaluation,
    submit_feedback,
    triage_feedback,
)
from apps.mel.tracking.models import CorrectiveAction, CorrectiveActionStatus

pytestmark = pytest.mark.django_db

User = get_user_model()


def _channel(slug="ch-basic", type=FeedbackChannelType.BENEFICIARY):
    return FeedbackChannel.objects.create(
        type=type,
        name=f"Channel {slug}",
        slug=slug,
        token=FeedbackChannel.generate_token(),
        is_public=True,
    )


def test_submit_feedback_creates_new_submission():
    ch = _channel(slug="ch-new")
    sub, created = submit_feedback(
        channel=ch,
        narrative="Tests the platform works.",
        submitter_email="beneficiary@example.com",
        severity=FeedbackSeverity.LOW,
    )
    assert created is True
    assert sub.status == FeedbackStatus.NEW
    assert sub.severity == FeedbackSeverity.LOW
    assert FeedbackSubmission.objects.count() == 1


def test_submit_feedback_is_idempotent_with_submission_key():
    ch = _channel(slug="ch-idem")
    kwargs = dict(
        channel=ch,
        narrative="Same event twice.",
        submission_key="webhook-evt-42",
        severity=FeedbackSeverity.LOW,
    )
    first, created_first = submit_feedback(**kwargs)
    second, created_second = submit_feedback(**kwargs)

    assert created_first is True
    assert created_second is False
    assert first.pk == second.pk
    assert FeedbackSubmission.objects.count() == 1


def test_high_severity_submission_autotriages_without_linked_subject():
    """When the channel has no subject, auto-triage marks as triaged but does
    NOT create a corrective action (nothing to attach it to)."""
    ch = _channel(slug="ch-high-nosub")

    sub, _ = submit_feedback(
        channel=ch,
        narrative="Critical defect.",
        severity=FeedbackSeverity.CRITICAL,
    )
    sub.refresh_from_db()

    assert sub.status == FeedbackStatus.TRIAGED
    assert sub.corrective_action is None
    assert CorrectiveAction.objects.count() == 0


def test_high_severity_with_subject_creates_corrective_action():
    """When the channel has a subject (e.g. a Report), high-severity feedback
    auto-triages and spawns a linked CorrectiveAction."""
    from apps.mel.reports.models import Report, ReportTemplate
    from datetime import date
    from apps.mel.indicators.models import LogFrame

    lf = LogFrame.objects.create(name="Feedback LF", slug="feedback-lf")
    template = ReportTemplate.objects.create(
        name="Feedback Q", slug="feedback-q", logframe=lf,
    )
    report = Report.objects.create(
        template=template,
        period_label="2026-Q2",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 6, 30),
    )
    from django.contrib.contenttypes.models import ContentType
    ct = ContentType.objects.get_for_model(Report)
    ch = FeedbackChannel.objects.create(
        type=FeedbackChannelType.REPORT_REVIEW,
        name="Channel with subject",
        slug="ch-with-subject",
        token=FeedbackChannel.generate_token(),
        subject_type=ct,
        subject_id=report.pk,
    )

    sub, _ = submit_feedback(
        channel=ch,
        narrative="Report is inaccurate — please fix.",
        severity=FeedbackSeverity.HIGH,
    )
    sub.refresh_from_db()

    assert sub.status == FeedbackStatus.TRIAGED
    assert sub.corrective_action_id is not None
    action = sub.corrective_action
    assert action.status == CorrectiveActionStatus.OPEN
    assert action.subject_type_id == ct.pk
    assert action.subject_id == report.pk


def test_triage_feedback_is_noop_on_already_actioned():
    ch = _channel(slug="ch-noop")
    sub, _ = submit_feedback(
        channel=ch,
        narrative="Low sev.",
        severity=FeedbackSeverity.LOW,
    )
    # Idempotent re-triage should not create a 2nd CorrectiveAction.
    triage_feedback(sub, triaged_by=None)
    triage_feedback(sub, triaged_by=None)
    assert CorrectiveAction.objects.count() == 0


def test_ingest_course_evaluation_is_idempotent_per_period():
    row1 = ingest_course_evaluation(
        source_system="moodle",
        course_slug="intro-agri",
        period_label="2026-Q2",
        course_name="Introduction to agri-food",
        aggregate_score=4.2,
        response_count=35,
        payload={"by_question": {"q1": 4.1}},
    )
    row2 = ingest_course_evaluation(
        source_system="moodle",
        course_slug="intro-agri",
        period_label="2026-Q2",
        course_name="Introduction to agri-food",
        aggregate_score=4.4,  # refreshed
        response_count=40,
        payload={"by_question": {"q1": 4.3}},
    )
    assert row1.pk == row2.pk
    row2.refresh_from_db()
    assert float(row2.aggregate_score) == pytest.approx(4.4)
    assert row2.response_count == 40
