"""
Property-based tests for schema generation.

Tests that verify schema generation works correctly for all models,
regardless of their structure.
"""

import pytest
from typing import Type
from pydantic import BaseModel

from envipy_additional_information import registry, EnviPyModel
from epapi.utils.schema_transformers import build_rjsf_output


class TestSchemaGeneration:
    """Test that all models can generate valid RJSF schemas."""

    @pytest.mark.parametrize("model_name,model_cls", list(registry.list_models().items()))
    def test_all_models_generate_rjsf(self, model_name: str, model_cls: Type[BaseModel]):
        """Every model in the registry should generate valid RJSF format."""
        # Skip non-EnviPyModel classes (parsers, etc.)
        if not issubclass(model_cls, EnviPyModel):
            pytest.skip(f"{model_name} is not an EnviPyModel")

        # Should not raise exception
        result = build_rjsf_output(model_cls)

        # Verify structure
        assert isinstance(result, dict), f"{model_name}: Result should be a dict"
        assert "schema" in result, f"{model_name}: Missing 'schema' key"
        assert "uiSchema" in result, f"{model_name}: Missing 'uiSchema' key"
        assert "formData" in result, f"{model_name}: Missing 'formData' key"
        assert "groups" in result, f"{model_name}: Missing 'groups' key"

        # Verify types
        assert isinstance(result["schema"], dict), f"{model_name}: schema should be dict"
        assert isinstance(result["uiSchema"], dict), f"{model_name}: uiSchema should be dict"
        assert isinstance(result["formData"], dict), f"{model_name}: formData should be dict"
        assert isinstance(result["groups"], list), f"{model_name}: groups should be list"

        # Verify schema has properties
        assert "properties" in result["schema"], f"{model_name}: schema should have 'properties'"
        assert isinstance(result["schema"]["properties"], dict), (
            f"{model_name}: properties should be dict"
        )

    @pytest.mark.parametrize("model_name,model_cls", list(registry.list_models().items()))
    def test_ui_schema_matches_schema_fields(self, model_name: str, model_cls: Type[BaseModel]):
        """uiSchema keys should match schema properties (or be nested for intervals)."""
        if not issubclass(model_cls, EnviPyModel):
            pytest.skip(f"{model_name} is not an EnviPyModel")

        result = build_rjsf_output(model_cls)
        schema_props = set(result["schema"]["properties"].keys())
        ui_schema_keys = set(result["uiSchema"].keys())

        # uiSchema should have entries for all top-level properties
        # (intervals may have nested start/end, but the main field should be present)
        assert ui_schema_keys.issubset(schema_props), (
            f"{model_name}: uiSchema has keys not in schema: {ui_schema_keys - schema_props}"
        )

    @pytest.mark.parametrize("model_name,model_cls", list(registry.list_models().items()))
    def test_groups_is_list_of_strings(self, model_name: str, model_cls: Type[BaseModel]):
        """Groups should be a list of strings."""
        if not issubclass(model_cls, EnviPyModel):
            pytest.skip(f"{model_name} is not an EnviPyModel")

        result = build_rjsf_output(model_cls)
        groups = result["groups"]

        assert isinstance(groups, list), f"{model_name}: groups should be list"
        assert all(isinstance(g, str) for g in groups), (
            f"{model_name}: all groups should be strings, got {groups}"
        )

    @pytest.mark.parametrize("model_name,model_cls", list(registry.list_models().items()))
    def test_form_data_matches_schema(self, model_name: str, model_cls: Type[BaseModel]):
        """formData keys should match schema properties."""
        if not issubclass(model_cls, EnviPyModel):
            pytest.skip(f"{model_name} is not an EnviPyModel")

        result = build_rjsf_output(model_cls)
        schema_props = set(result["schema"]["properties"].keys())
        form_data_keys = set(result["formData"].keys())

        # formData should only contain keys that are in schema
        assert form_data_keys.issubset(schema_props), (
            f"{model_name}: formData has keys not in schema: {form_data_keys - schema_props}"
        )


class TestWidgetTypes:
    """Test that widget types are valid."""

    @pytest.mark.parametrize("model_name,model_cls", list(registry.list_models().items()))
    def test_widget_types_are_valid(self, model_name: str, model_cls: Type[BaseModel]):
        """All widget types in uiSchema should be valid WidgetType values."""
        from envipy_additional_information.ui_config import WidgetType

        if not issubclass(model_cls, EnviPyModel):
            pytest.skip(f"{model_name} is not an EnviPyModel")

        result = build_rjsf_output(model_cls)
        valid_widgets = {wt.value for wt in WidgetType}

        for field_name, ui_config in result["uiSchema"].items():
            widget = ui_config.get("ui:widget")
            if widget:
                assert widget in valid_widgets, (
                    f"{model_name}.{field_name}: Invalid widget '{widget}'. Valid: {valid_widgets}"
                )
