"""Regression tests for three survey-take / results fixes.

Covers:
  * Bug 1 — rating scale honours the configured ``{min, max}`` range.
  * Bug 2 — boolean analytics expose a real ``no_pct`` (complement of yes).
  * Bug 3 — ``SurveyTakeView.post`` enforces ``required`` server-side.
"""
from __future__ import annotations

import pytest
from django.test import Client
from django.urls import reverse

from apps.mel.feedback.models import (
    Survey,
    SurveyQuestion,
    SurveyQuestionType,
    SurveyResponse,
)
from apps.mel.feedback.services import activate_survey, analyse_responses, collect_response

pytestmark = pytest.mark.django_db


def _survey_with(rating_options=None):
    survey = Survey.objects.create(title="QA survey", slug="qa-survey")
    short = SurveyQuestion.objects.create(
        survey=survey, ordinal=0, page=1,
        type=SurveyQuestionType.SHORT_ANSWER, text="Free thoughts?", required=False,
    )
    rating = SurveyQuestion.objects.create(
        survey=survey, ordinal=1, page=1,
        type=SurveyQuestionType.RATING, text="Rate us", required=True,
        options=rating_options if rating_options is not None else {"min": 1, "max": 5},
    )
    boolean = SurveyQuestion.objects.create(
        survey=survey, ordinal=2, page=1,
        type=SurveyQuestionType.BOOLEAN, text="Recommend?", required=True,
    )
    activate_survey(survey)
    return survey, short, rating, boolean


# --- Bug 1 -----------------------------------------------------------------


def test_rating_points_honours_configured_range():
    q = SurveyQuestion(type=SurveyQuestionType.RATING, text="x", options={"min": 1, "max": 10})
    assert q.rating_points() == list(range(1, 11))

    q0 = SurveyQuestion(type=SurveyQuestionType.RATING, text="x", options={"min": 0, "max": 5})
    assert q0.rating_points() == [0, 1, 2, 3, 4, 5]


def test_rating_points_defaults_when_options_missing():
    q = SurveyQuestion(type=SurveyQuestionType.RATING, text="x", options=[])
    assert q.rating_points() == [1, 2, 3, 4, 5]


def test_rating_take_page_renders_configured_scale():
    survey, _short, rating, _bool = _survey_with(rating_options={"min": 1, "max": 10})
    url = reverse("mel_feedback:survey_take_channel", args=[survey.channel.token])
    html = Client(SERVER_NAME="localhost").get(url).content.decode()
    for n in range(1, 11):
        assert f'value="{n}"' in html


# --- Bug 2 -----------------------------------------------------------------


def test_boolean_analytics_expose_proportional_no_pct():
    survey, short, rating, boolean = _survey_with()
    # Two "no" and one "yes".
    for val in ("false", "false", "true"):
        collect_response(
            survey,
            answers={rating.pk: "3", boolean.pk: val},
            complete=True,
        )
    payload = analyse_responses(survey)["per_question"][boolean.pk]
    assert payload["yes_count"] == 1
    assert payload["no_count"] == 2
    assert payload["yes_pct"] == pytest.approx(33.3)
    assert payload["no_pct"] == pytest.approx(66.7)


# --- Bug 3 -----------------------------------------------------------------


def test_post_rejects_missing_required_answer():
    survey, short, rating, boolean = _survey_with()
    url = reverse("mel_feedback:survey_take_channel", args=[survey.channel.token])
    client = Client(SERVER_NAME="localhost")
    # Omit the required rating + boolean answers.
    resp = client.post(url, {"page": "1", f"answer_{short.pk}": "hi"})
    assert resp.status_code == 200
    assert b"Please answer all required questions" in resp.content
    assert SurveyResponse.objects.filter(survey=survey).count() == 0


def test_post_accepts_when_required_answered():
    survey, short, rating, boolean = _survey_with()
    url = reverse("mel_feedback:survey_take_channel", args=[survey.channel.token])
    client = Client(SERVER_NAME="localhost")
    resp = client.post(
        url,
        {"page": "1", f"answer_{rating.pk}": "4", f"answer_{boolean.pk}": "true"},
    )
    assert resp.status_code == 200
    assert SurveyResponse.objects.filter(survey=survey).count() == 1
