import pytest
from pydantic import ValidationError

from a3api import (
    AssessAgeRequest,
    AssessAgeResponse,
    OsSignal,
    Verdict,
    AgeBracket,
    BehavioralMetrics,
    DeviceContext,
    FaceEstimationResult,
    FaceEstimationProvider,
    AccountLongevity,
    InputComplexity,
    ContextualSignals,
    IpType,
    ReferrerCategory,
    ParentalConsentStatus,
    ConsentSource,
)


class TestAssessAgeRequest:
    def test_minimal_request(self):
        req = AssessAgeRequest(
            os_signal=OsSignal.AGE_18_PLUS,
            user_country_code="US",
        )
        assert req.os_signal == OsSignal.AGE_18_PLUS
        assert req.user_country_code == "US"
        assert req.behavioral_metrics is None

    def test_full_request(self):
        req = AssessAgeRequest(
            os_signal=OsSignal.NOT_AVAILABLE,
            user_country_code="US",
            behavioral_metrics=BehavioralMetrics(
                avg_touch_precision=0.72,
                scroll_velocity=1200,
                form_completion_time_ms=8500,
                is_autofill_detected=False,
                touch_pressure_variance=0.38,
                multi_touch_frequency=3.5,
                face_estimation_result=FaceEstimationResult(
                    estimation_provider=FaceEstimationProvider.YOTI,
                    estimated_age_lower=13,
                    estimated_age_upper=17,
                    confidence=0.82,
                ),
            ),
            device_context=DeviceContext(
                os_version="iOS 17.2",
                device_model="iPhone 15 Pro",
                is_high_contrast_enabled=False,
                screen_scale_factor=1.0,
            ),
            contextual_signals=ContextualSignals(
                ip_type=IpType.RESIDENTIAL,
                timezone_offset_delta_minutes=0,
                referrer_category=ReferrerCategory.DIRECT,
            ),
            account_longevity=AccountLongevity(account_age_days=730),
            input_complexity=InputComplexity(
                keyboard_autocorrect_rate=0.18,
                average_word_complexity_score=0.55,
            ),
            parental_consent_status=ParentalConsentStatus.APPROVED,
            consent_source=ConsentSource.OS_SYSTEM,
        )
        assert req.behavioral_metrics is not None
        assert req.behavioral_metrics.face_estimation_result is not None
        assert req.behavioral_metrics.face_estimation_result.estimation_provider == FaceEstimationProvider.YOTI

    def test_invalid_os_signal_raises(self):
        with pytest.raises(ValidationError):
            AssessAgeRequest(os_signal="invalid", user_country_code="US")

    def test_model_dump_excludes_none(self):
        req = AssessAgeRequest(
            os_signal=OsSignal.AGE_18_PLUS,
            user_country_code="US",
        )
        d = req.model_dump(exclude_none=True)
        assert "behavioral_metrics" not in d
        assert d["os_signal"] == "18-plus"

    def test_string_values_for_enums(self):
        """String values should be accepted in place of enum instances."""
        req = AssessAgeRequest(
            os_signal="18-plus",
            user_country_code="US",
        )
        assert req.os_signal == OsSignal.AGE_18_PLUS


class TestAssessAgeResponse:
    def test_parse_full_response(self):
        data = {
            "confidence_score": 0.92,
            "verdict": "CONSISTENT",
            "os_signal_age_bracket": "18-plus",
            "assessed_age_bracket": "18-plus",
            "signal_overridden": False,
            "internal_evidence_only": True,
            "evidence_tags": ["os_signal_consistent"],
            "user_country_code": "US",
            "verification_token": "eyJ0ZXN0IjoiMSJ9.abc123",
        }
        resp = AssessAgeResponse.model_validate(data)
        assert resp.verdict == Verdict.CONSISTENT
        assert resp.assessed_age_bracket == AgeBracket.AGE_18_PLUS
        assert resp.parental_consent_status is None

    def test_parse_response_with_consent(self):
        data = {
            "confidence_score": 0.85,
            "verdict": "OVERRIDE",
            "os_signal_age_bracket": "18-plus",
            "assessed_age_bracket": "under-13",
            "signal_overridden": True,
            "internal_evidence_only": False,
            "evidence_tags": ["high_motor_precision_delta", "signal_override_active"],
            "user_country_code": "US",
            "verification_token": "token123",
            "parental_consent_status": "approved",
            "consent_source": "os-system",
        }
        resp = AssessAgeResponse.model_validate(data)
        assert resp.parental_consent_status == ParentalConsentStatus.APPROVED
        assert resp.consent_source == ConsentSource.OS_SYSTEM

    def test_invalid_verdict_raises(self):
        with pytest.raises(ValidationError):
            AssessAgeResponse.model_validate({
                "confidence_score": 0.5,
                "verdict": "INVALID",
                "os_signal_age_bracket": "18-plus",
                "assessed_age_bracket": "18-plus",
                "signal_overridden": False,
                "internal_evidence_only": True,
                "evidence_tags": [],
                "user_country_code": "US",
                "verification_token": "tok",
            })


class TestBehavioralMetrics:
    def test_range_validation(self):
        with pytest.raises(ValidationError):
            BehavioralMetrics(
                avg_touch_precision=1.5,  # > 1
                scroll_velocity=1200,
                form_completion_time_ms=8500,
            )

    def test_negative_scroll_velocity(self):
        with pytest.raises(ValidationError):
            BehavioralMetrics(
                avg_touch_precision=0.5,
                scroll_velocity=-1,
                form_completion_time_ms=8500,
            )
