"""
UI Schema Alignment Tests.

Adversarial tests that verify Pydantic UI schemas in lightwave-core
match TypeScript interfaces in lightwave-ui.

Strategy: REJECTION TESTS FIRST
- What should FAIL (missing schemas, type mismatches)
- Then verify alignment passes

Source of Truth: TypeScript interfaces in lightwave-ui/src/

Run with:
    pytest lightwave/schema/testing/ui_alignment_tests.py -v
"""

from __future__ import annotations

import pytest

from lightwave.schema.ui_alignment import (
    UIAlignmentChecker,
    UIAlignmentReport,
)


@pytest.fixture(scope="module")
def ui_alignment_report() -> UIAlignmentReport:
    """
    Run alignment check once per test module.

    Module-scoped to avoid repeated expensive scanning.
    """
    checker = UIAlignmentChecker()
    return checker.check_all()


@pytest.fixture(scope="module")
def alignment_checker() -> UIAlignmentChecker:
    """Provide checker instance for detailed tests."""
    return UIAlignmentChecker()


class TestUISchemaRejection:
    """
    Tests that verify misalignment is DETECTED.

    These are adversarial tests - they ensure the checker
    catches problems when they exist.
    """

    def test_type_mismatches_detected(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Verify type mismatches are caught and reported."""
        mismatches = [i for i in ui_alignment_report.issues if i.category == "type_mismatch"]
        # If mismatches exist, they should be errors (not silently ignored)
        for mismatch in mismatches:
            assert mismatch.severity == "error", f"Type mismatch should be error severity: {mismatch}"

    def test_property_mismatches_detected(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Verify missing properties are caught."""
        missing = [i for i in ui_alignment_report.issues if i.category == "property_missing"]
        # Missing properties should be errors
        for issue in missing:
            assert issue.severity == "error", f"Missing property should be error severity: {issue}"

    def test_orphan_schemas_are_warnings(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Verify orphan Pydantic schemas are warnings, not errors."""
        orphans = [i for i in ui_alignment_report.issues if i.category == "orphan_pydantic"]
        # Orphans may be intentional, so should be warnings
        for issue in orphans:
            assert issue.severity == "warning", f"Orphan schema should be warning severity: {issue}"


class TestUISchemaAlignment:
    """
    Tests that verify positive alignment.

    These tests pass when schemas are correctly aligned.
    """

    def test_pydantic_schemas_discovered(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Verify Pydantic schemas are being discovered."""
        assert ui_alignment_report.pydantic_schemas_count > 0, "No Pydantic schemas discovered - check module path"

    def test_typescript_interfaces_discovered(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Verify TypeScript interfaces are being parsed."""
        # This may fail if lightwave-ui is not available
        # In CI, both packages should be checked out
        if ui_alignment_report.ts_interfaces_count == 0:
            pytest.skip("No TypeScript interfaces found - lightwave-ui may not be available")

    def test_no_type_mismatches(self, ui_alignment_report: UIAlignmentReport) -> None:
        """All Pydantic types must match TypeScript types."""
        mismatches = [i for i in ui_alignment_report.issues if i.category == "type_mismatch"]
        assert len(mismatches) == 0, "Type mismatches found:\n" + "\n".join(f"  - {m}" for m in mismatches)

    def test_no_missing_properties(self, ui_alignment_report: UIAlignmentReport) -> None:
        """All TypeScript properties must exist in Pydantic."""
        missing = [i for i in ui_alignment_report.issues if i.category == "property_missing"]
        assert len(missing) == 0, "Missing properties found:\n" + "\n".join(f"  - {m}" for m in missing)

    def test_no_alias_mismatches(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Serialization aliases must correctly map snake_case to camelCase."""
        alias_issues = [i for i in ui_alignment_report.issues if i.category == "alias_mismatch"]
        assert len(alias_issues) == 0, "Alias mismatches found:\n" + "\n".join(f"  - {a}" for a in alias_issues)

    def test_alignment_report_is_valid(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Report should be well-formed and serializable."""
        # Test to_dict serialization
        report_dict = ui_alignment_report.to_dict()
        assert "aligned" in report_dict
        assert "error_count" in report_dict
        assert "issues" in report_dict
        assert isinstance(report_dict["issues"], list)

        # Test summary generation
        summary = ui_alignment_report.summary()
        assert "UI Schema Alignment Report" in summary


class TestSpecificSchemas:
    """
    Tests for specific critical schemas.

    These schemas are essential for the UI and must always be aligned.
    """

    CRITICAL_SCHEMAS = [
        "HeaderProps",
        "CTAProps",
        "BadgeProps",
        "PersonInfo",
    ]

    @pytest.mark.parametrize("schema_name", CRITICAL_SCHEMAS)
    def test_critical_schema_exists(
        self,
        alignment_checker: UIAlignmentChecker,
        schema_name: str,
    ) -> None:
        """Critical schemas must exist in Pydantic."""
        schemas = alignment_checker.pydantic_discovery.discover_all()
        if schema_name not in schemas:
            pytest.skip(f"{schema_name} not yet implemented")

    @pytest.mark.parametrize("schema_name", CRITICAL_SCHEMAS)
    def test_critical_schema_aligned(
        self,
        ui_alignment_report: UIAlignmentReport,
        schema_name: str,
    ) -> None:
        """Critical schemas must have zero alignment errors."""
        errors = [i for i in ui_alignment_report.issues if i.interface_name == schema_name and i.severity == "error"]
        assert len(errors) == 0, f"{schema_name} has alignment errors:\n" + "\n".join(f"  - {e}" for e in errors)


class TestCheckerFunctionality:
    """
    Tests for checker internal functionality.

    These tests verify the checker itself works correctly.
    """

    def test_type_mapping_string(self, alignment_checker: UIAlignmentChecker) -> None:
        """TypeScript 'string' should map to Python 'str'."""
        assert alignment_checker._types_compatible("string", "str")
        assert alignment_checker._types_compatible("string", "str | None")

    def test_type_mapping_number(self, alignment_checker: UIAlignmentChecker) -> None:
        """TypeScript 'number' should map to Python 'int' or 'float'."""
        assert alignment_checker._types_compatible("number", "int")
        assert alignment_checker._types_compatible("number", "float")
        assert alignment_checker._types_compatible("number", "int | None")

    def test_type_mapping_boolean(self, alignment_checker: UIAlignmentChecker) -> None:
        """TypeScript 'boolean' should map to Python 'bool'."""
        assert alignment_checker._types_compatible("boolean", "bool")
        assert alignment_checker._types_compatible("boolean", "bool | None")

    def test_type_mapping_array(self, alignment_checker: UIAlignmentChecker) -> None:
        """TypeScript arrays should map to Python lists."""
        assert alignment_checker._types_compatible("string[]", "list[str]")
        assert alignment_checker._types_compatible("number[]", "list[int]")
        assert alignment_checker._types_compatible("Array<string>", "list[str]")

    def test_type_mapping_props(self, alignment_checker: UIAlignmentChecker) -> None:
        """Props types should match by name."""
        assert alignment_checker._types_compatible("HeaderProps", "HeaderProps")
        assert not alignment_checker._types_compatible("HeaderProps", "FooterProps")

    def test_type_mapping_optional(self, alignment_checker: UIAlignmentChecker) -> None:
        """Optional types should be handled correctly."""
        assert alignment_checker._types_compatible("string | undefined", "str | None")
        assert alignment_checker._types_compatible("string | null", "str | None")


class TestReportGeneration:
    """Tests for report generation and output."""

    def test_summary_contains_counts(self, ui_alignment_report: UIAlignmentReport) -> None:
        """Summary should include interface/schema counts."""
        summary = ui_alignment_report.summary()
        assert "TypeScript Interfaces:" in summary
        assert "Pydantic Schemas:" in summary
        assert "Errors:" in summary
        assert "Warnings:" in summary

    def test_is_aligned_property(self, ui_alignment_report: UIAlignmentReport) -> None:
        """is_aligned should reflect error count."""
        expected_aligned = ui_alignment_report.error_count == 0
        assert ui_alignment_report.is_aligned == expected_aligned
