"""End-to-end: publish report → feedback channel seeded → high-severity
feedback submission → linked CorrectiveAction appears.

Verifies FRMFL033 (reporting → soliciting feedback → adaptive management).
"""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType

from apps.mel.feedback.models import (
    FeedbackChannel,
    FeedbackChannelType,
    FeedbackSeverity,
    FeedbackStatus,
)
from apps.mel.feedback.services import submit_feedback
from apps.mel.indicators.models import (
    Indicator,
    IndicatorFrequency,
    IndicatorTarget,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)
from apps.mel.indicators.services import record_data_point
from apps.mel.reports.models import Report, ReportTemplate
from apps.mel.reports.services import generate_report, publish_report
from apps.mel.tracking.models import CorrectiveAction, CorrectiveActionStatus

# transaction=True to let on_commit callbacks (signal dispatch) fire.
pytestmark = pytest.mark.django_db(transaction=True)

User = get_user_model()


def test_publishing_report_seeds_channel_and_feedback_spawns_corrective_action():
    officer = User.objects.create_user(email="officer-fb@example.com", password="x")

    lf = LogFrame.objects.create(name="Loop LF", slug="loop-lf", owner=officer)
    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)
    ind = Indicator.objects.create(
        logframe_row=output,
        code="loop-ind",
        name="Loop indicator",
        data_source="m",
        unit="count",
        calculation_method="count",
        frequency=IndicatorFrequency.MONTHLY,
    )
    IndicatorTarget.objects.create(
        indicator=ind,
        period_label="2026-04",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 4, 30),
        baseline_value=Decimal("0"),
        target_value=Decimal("100"),
    )
    record_data_point(
        indicator=ind, value=80, period_label="2026-04", auto_verify=True,
        source_module="m", source_event="e", source_object_id="1",
    )

    template = ReportTemplate.objects.create(
        name="Loop Q",
        slug="loop-q",
        logframe=lf,
        sections=["indicator_rollup", "compliance"],
    )
    report = generate_report(
        template=template,
        period_label="2026-04",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 4, 30),
        user=officer,
    )

    # Before publish — no feedback channel for this report exists yet.
    report_ct = ContentType.objects.get_for_model(Report)
    assert not FeedbackChannel.objects.filter(
        subject_type=report_ct, subject_id=report.pk,
    ).exists()

    # Publish → receiver should seed a ReportReview channel.
    publish_report(report)
    channel = FeedbackChannel.objects.get(subject_type=report_ct, subject_id=report.pk)
    assert channel.type == FeedbackChannelType.REPORT_REVIEW

    # Stakeholder submits high-severity feedback.
    submission, _ = submit_feedback(
        channel=channel,
        narrative="Indicator X ignores regional context.",
        submitter_email="stakeholder@example.com",
        severity=FeedbackSeverity.HIGH,
    )
    submission.refresh_from_db()

    # Auto-triage + CorrectiveAction creation (feedback → adaptive management).
    assert submission.status == FeedbackStatus.TRIAGED
    assert submission.corrective_action_id is not None
    action = submission.corrective_action
    assert action.status == CorrectiveActionStatus.OPEN
    assert action.subject_type_id == report_ct.pk
    assert action.subject_id == report.pk

    # Exactly one action spawned — idempotency check.
    assert CorrectiveAction.objects.filter(
        subject_type=report_ct, subject_id=report.pk,
    ).count() == 1
