# Feature: trading-feedback-engine, Property 3: Validation discrepancy detection correctness
"""Property-based tests for report validation discrepancy detection.

Feature: trading-feedback-engine

Tests the validation discrepancy detection correctness property from the
design specification: for any pair of computed metric value and snapshot
metric value (both finite, non-negative floats), the validation function
SHALL produce a warning if and only if the percentage difference exceeds 5%.
The percentage difference SHALL be computed as |computed - snapshot| /
snapshot * 100 when snapshot > 0, and SHALL flag any non-zero computed value
when snapshot is 0.
"""
from __future__ import annotations

import math

from hypothesis import given, settings
from hypothesis import strategies as st

from services.reporting.validator import (
    DISCREPANCY_THRESHOLD_PCT,
    _check_discrepancy,
)

# ---------------------------------------------------------------------------
# Property 3: Validation Discrepancy Detection Correctness
# Validates: Requirements 4.1, 4.2, 4.3, 4.4
# ---------------------------------------------------------------------------

# Strategy: finite, non-negative floats in [0, 1e6]
_metric_float = st.floats(
    min_value=0, max_value=1e6, allow_nan=False, allow_infinity=False,
)


@given(computed=_metric_float, snapshot=_metric_float)
@settings(max_examples=100)
def test_discrepancy_detection_correctness(
    computed: float,
    snapshot: float,
) -> None:
    """**Validates: Requirements 4.1, 4.2, 4.3, 4.4**

    For any pair of computed and snapshot values (finite, non-negative):
    - Both zero → no warning
    - Snapshot zero, computed non-zero → warning (100% discrepancy)
    - Snapshot > 0 → warning iff |computed - snapshot| / snapshot * 100 > 5%
    """
    result = _check_discrepancy("test_field", computed, snapshot)

    if snapshot == 0.0 and computed == 0.0:
        # Both zero → no discrepancy
        assert result is None, (
            f"Expected no warning when both values are 0, got {result}"
        )
    elif snapshot == 0.0:
        # Non-zero computed with zero snapshot → always a warning
        assert result is not None, (
            f"Expected warning for non-zero computed={computed} with "
            f"snapshot=0, got None"
        )
        assert result.pct_difference == 100.0, (
            f"Expected 100% discrepancy for zero snapshot, "
            f"got {result.pct_difference}%"
        )
    else:
        # Normal case: snapshot > 0
        expected_pct = abs(computed - snapshot) / snapshot * 100.0
        if expected_pct > DISCREPANCY_THRESHOLD_PCT:
            assert result is not None, (
                f"Expected warning for {expected_pct:.4f}% discrepancy "
                f"(computed={computed}, snapshot={snapshot}), got None"
            )
            # When expected_pct is inf (very small snapshot), both should be inf
            if math.isinf(expected_pct):
                assert math.isinf(result.pct_difference), (
                    f"Expected inf pct_difference, got {result.pct_difference}"
                )
            else:
                assert abs(result.pct_difference - round(expected_pct, 4)) < 1e-6, (
                    f"Percentage difference mismatch: "
                    f"expected {round(expected_pct, 4)}, "
                    f"got {result.pct_difference}"
                )
        else:
            assert result is None, (
                f"Expected no warning for {expected_pct:.4f}% discrepancy "
                f"(computed={computed}, snapshot={snapshot}), "
                f"got warning with pct_difference={result.pct_difference}"
            )


@given(computed=_metric_float, snapshot=_metric_float)
@settings(max_examples=100)
def test_discrepancy_threshold_is_five_percent(
    computed: float,
    snapshot: float,
) -> None:
    """**Validates: Requirements 4.1, 4.2, 4.3, 4.4**

    Verify that DISCREPANCY_THRESHOLD_PCT = 5.0 is the threshold used:
    the function produces a warning if and only if the discrepancy
    exceeds exactly 5%.
    """
    assert DISCREPANCY_THRESHOLD_PCT == 5.0, (
        f"Expected threshold of 5.0%, got {DISCREPANCY_THRESHOLD_PCT}%"
    )

    result = _check_discrepancy("threshold_check", computed, snapshot)

    if snapshot == 0.0 and computed == 0.0:
        assert result is None
    elif snapshot == 0.0:
        # 100% > 5% → always warning
        assert result is not None
    else:
        pct = abs(computed - snapshot) / snapshot * 100.0
        should_warn = pct > 5.0
        if should_warn:
            assert result is not None, (
                f"Discrepancy {pct:.4f}% > 5% but no warning produced"
            )
        else:
            assert result is None, (
                f"Discrepancy {pct:.4f}% <= 5% but warning produced"
            )
