"""G9 — structured ValidationRuleForm + human_readable_params.

Covers the non-technical form refactor that replaced the JSON-textarea with
per-rule-type structured fields. The form ``clean`` is the contract under
test: it must assemble the same ``params`` dict shape that
``services.validate_value`` consumes at runtime.
"""
from __future__ import annotations

import pytest

from apps.mel.indicators.forms import ValidationRuleForm
from apps.mel.indicators.models import (
    Indicator,
    IndicatorFrequency,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
    ValidationRule,
)

pytestmark = pytest.mark.django_db


def _build_indicator(code: str = "g9-form-a"):
    lf = LogFrame.objects.create(name=f"LF {code}", slug=f"lf-{code}")
    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)
    return Indicator.objects.create(
        logframe_row=output, code=code, name=code.upper(),
        unit="%", calculation_method="mean", data_source="manual",
        frequency=IndicatorFrequency.QUARTERLY,
    )


def _save(form, indicator):
    rule = form.save(commit=False)
    rule.indicator = indicator
    rule.save()
    return rule


def test_range_form_builds_min_max_params():
    ind = _build_indicator("g9-form-range")
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.RANGE,
        "range_min": "0",
        "range_max": "100",
        "is_active": "on",
    })
    assert form.is_valid(), form.errors
    rule = _save(form, ind)
    assert rule.params == {"min": 0.0, "max": 100.0}


def test_range_form_accepts_only_min():
    ind = _build_indicator("g9-form-range-min")
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.RANGE,
        "range_min": "0",
        "is_active": "on",
    })
    assert form.is_valid(), form.errors
    rule = _save(form, ind)
    assert rule.params == {"min": 0.0}


def test_range_form_rejects_both_blank():
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.RANGE,
        "is_active": "on",
    })
    assert not form.is_valid()
    assert "range_min" in form.errors


def test_range_form_rejects_min_greater_than_max():
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.RANGE,
        "range_min": "10",
        "range_max": "5",
        "is_active": "on",
    })
    assert not form.is_valid()
    assert "range_max" in form.errors


def test_regex_form_builds_pattern_params():
    ind = _build_indicator("g9-form-regex")
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.REGEX,
        "regex_pattern": "^[A-Z]+$",
        "is_active": "on",
    })
    assert form.is_valid(), form.errors
    rule = _save(form, ind)
    assert rule.params == {"pattern": "^[A-Z]+$"}


def test_regex_form_rejects_invalid_pattern():
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.REGEX,
        "regex_pattern": "[unclosed",
        "is_active": "on",
    })
    assert not form.is_valid()
    assert "regex_pattern" in form.errors


def test_comparison_form_builds_op_other_params():
    ind = _build_indicator("g9-form-cmp")
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.COMPARISON,
        "comparison_op": ">=",
        "comparison_other": "50",
        "is_active": "on",
    })
    assert form.is_valid(), form.errors
    rule = _save(form, ind)
    assert rule.params == {"op": ">=", "other": 50.0}


def test_comparison_form_rejects_blank_op():
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.COMPARISON,
        "comparison_other": "50",
        "is_active": "on",
    })
    assert not form.is_valid()
    assert "comparison_op" in form.errors


def test_custom_expression_form_builds_expression_params():
    ind = _build_indicator("g9-form-expr")
    form = ValidationRuleForm({
        "rule_type": ValidationRule.RuleType.CUSTOM_EXPRESSION,
        "custom_expression": "value > 0 and value < previous",
        "is_active": "on",
    })
    assert form.is_valid(), form.errors
    rule = _save(form, ind)
    assert rule.params == {"expression": "value > 0 and value < previous"}


def test_form_without_rule_type_is_invalid():
    form = ValidationRuleForm({"is_active": "on"})
    assert not form.is_valid()
    assert "rule_type" in form.errors


def test_form_pre_fills_per_type_fields_from_instance():
    ind = _build_indicator("g9-form-prefill")
    rule = ValidationRule.objects.create(
        indicator=ind,
        rule_type=ValidationRule.RuleType.RANGE,
        params={"min": 0, "max": 100},
    )
    form = ValidationRuleForm(instance=rule)
    assert form.initial["range_min"] == 0
    assert form.initial["range_max"] == 100


def test_human_readable_params_range_between():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.RANGE, params={"min": 0, "max": 100})
    assert rule.human_readable_params() == "Between 0 and 100"


def test_human_readable_params_range_at_least():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.RANGE, params={"min": 0})
    assert rule.human_readable_params() == "At least 0"


def test_human_readable_params_range_at_most():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.RANGE, params={"max": 100})
    assert rule.human_readable_params() == "At most 100"


def test_human_readable_params_regex():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.REGEX, params={"pattern": "^[A-Z]+$"})
    assert rule.human_readable_params() == "Matches ^[A-Z]+$"


def test_human_readable_params_comparison():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.COMPARISON, params={"op": ">=", "other": 50})
    assert rule.human_readable_params() == "Must be ≥ 50"


def test_human_readable_params_custom_expression():
    rule = ValidationRule(
        rule_type=ValidationRule.RuleType.CUSTOM_EXPRESSION,
        params={"expression": "value > 0 and value < previous"},
    )
    assert rule.human_readable_params() == "Expression: value > 0 and value < previous"


def test_human_readable_params_defensive_on_missing_keys():
    rule = ValidationRule(rule_type=ValidationRule.RuleType.RANGE, params={})
    assert rule.human_readable_params() == "—"
