"""Unit tests for predicate compilation and DSL parsing."""

# FILE: tests/unit/coverage/test_predicate_compilation.py

import pytest

from libs.policy import PolicyLoader

# pylint: disable=wrong-import-position,import-error,too-few-public-methods,global-statement
# pylint: disable=raise-missing-from,unused-argument,too-many-arguments,too-many-positional-arguments
# pylint: disable=too-many-locals,import-outside-toplevel
# mypy: disable-error-code=union-attr
# mypy: disable-error-code=no-untyped-def


class TestPredicateCompilation:
    """Test predicate compilation and DSL parsing"""

    @pytest.fixture
    def policy_loader(self):
        """Create policy loader for testing"""
        return PolicyLoader()

    def test_compile_exists_condition(self, policy_loader):
        """Test compilation of exists() conditions"""
        condition = "exists(IncomeItem[type='Employment'])"
        predicate = policy_loader._compile_condition(condition)

        assert callable(predicate)
        result = predicate("T-001", "2024-25")
        assert isinstance(result, bool)

    def test_compile_exists_condition_with_filters(self, policy_loader):
        """Test exists() with complex filters"""
        condition = "exists(IncomeItem[type='SelfEmployment' AND turnover_lt_vat_threshold=true])"
        predicate = policy_loader._compile_condition(condition)

        assert callable(predicate)
        result = predicate("T-001", "2024-25")
        assert isinstance(result, bool)

    def test_compile_property_conditions(self, policy_loader):
        """Test compilation of property conditions"""
        conditions = [
            "property_joint_ownership",
            "candidate_FHL",
            "claims_FTCR",
            "claims_remittance_basis",
            "received_estate_income",
        ]

        for condition in conditions:
            predicate = policy_loader._compile_condition(condition)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_compile_computed_conditions(self, policy_loader):
        """Test compilation of computed conditions"""
        conditions = [
            "turnover_lt_vat_threshold",
            "turnover_ge_vat_threshold",
        ]

        for condition in conditions:
            predicate = policy_loader._compile_condition(condition)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_compile_taxpayer_flag_conditions(self, policy_loader):
        """Test compilation of taxpayer flag conditions"""
        condition = "taxpayer_flag:has_employment"
        predicate = policy_loader._compile_condition(condition)

        assert callable(predicate)
        result = predicate("T-001", "2024-25")
        assert isinstance(result, bool)

    def test_compile_filing_mode_conditions(self, policy_loader):
        """Test compilation of filing mode conditions"""
        condition = "filing_mode:paper"
        predicate = policy_loader._compile_condition(condition)

        assert callable(predicate)
        result = predicate("T-001", "2024-25")
        assert isinstance(result, bool)

    def test_compile_unknown_condition(self, policy_loader):
        """Test compilation of unknown condition defaults to False"""
        condition = "unknown_condition_type"
        predicate = policy_loader._compile_condition(condition)

        assert callable(predicate)
        result = predicate("T-001", "2024-25")
        assert result is False  # Unknown conditions default to False

    def test_exists_predicate_creation(self, policy_loader):
        """Test exists predicate creation with different entity types"""
        entity_types = [
            "IncomeItem",
            "ExpenseItem",
            "PropertyAsset",
            "TrustDistribution",
        ]

        for entity_type in entity_types:
            predicate = policy_loader._create_exists_predicate(
                entity_type, "type='test'"
            )
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_property_predicate_creation(self, policy_loader):
        """Test property predicate creation"""
        properties = [
            "property_joint_ownership",
            "candidate_FHL",
            "claims_FTCR",
        ]

        for prop in properties:
            predicate = policy_loader._create_property_predicate(prop)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_computed_predicate_creation(self, policy_loader):
        """Test computed predicate creation"""
        computations = [
            "turnover_lt_vat_threshold",
            "turnover_ge_vat_threshold",
        ]

        for comp in computations:
            predicate = policy_loader._create_computed_predicate(comp)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_flag_predicate_creation(self, policy_loader):
        """Test flag predicate creation"""
        flags = [
            "has_employment",
            "is_self_employed_short",
            "has_property_income",
            "has_foreign_income",
        ]

        for flag in flags:
            predicate = policy_loader._create_flag_predicate(flag)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_filing_mode_predicate_creation(self, policy_loader):
        """Test filing mode predicate creation"""
        modes = ["paper", "online", "agent"]

        for mode in modes:
            predicate = policy_loader._create_filing_mode_predicate(mode)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_exists_condition_regex_parsing(self, policy_loader):
        """Test regex parsing of exists conditions"""
        test_cases = [
            (
                "exists(IncomeItem[type='Employment'])",
                "IncomeItem",
                "type='Employment'",
            ),
            (
                "exists(ExpenseItem[category='FinanceCosts'])",
                "ExpenseItem",
                "category='FinanceCosts'",
            ),
            (
                "exists(PropertyAsset[joint_ownership=true])",
                "PropertyAsset",
                "joint_ownership=true",
            ),
        ]

        for condition, expected_entity, expected_filters in test_cases:
            # Test that the regex matches correctly
            import re

            exists_match = re.match(r"exists\((\w+)\[([^\]]+)\]\)", condition)
            assert exists_match is not None
            assert exists_match.group(1) == expected_entity
            assert exists_match.group(2) == expected_filters

    def test_condition_whitespace_handling(self, policy_loader):
        """Test that conditions handle whitespace correctly"""
        conditions_with_whitespace = [
            "  exists(IncomeItem[type='Employment'])  ",
            "\tproperty_joint_ownership\t",
            "\n taxpayer_flag:has_employment \n",
        ]

        for condition in conditions_with_whitespace:
            predicate = policy_loader._compile_condition(condition)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_complex_exists_filters(self, policy_loader):
        """Test exists conditions with complex filter expressions"""
        complex_conditions = [
            "exists(IncomeItem[type='SelfEmployment' AND turnover_lt_vat_threshold=true])",
            "exists(ExpenseItem[category='CapitalAllowances'])",
            "exists(IncomeItem[type IN ['ForeignInterest','ForeignDividends']])",
        ]

        for condition in complex_conditions:
            predicate = policy_loader._compile_condition(condition)
            assert callable(predicate)
            result = predicate("T-001", "2024-25")
            assert isinstance(result, bool)

    def test_predicate_consistency(self, policy_loader):
        """Test that predicates return consistent results for same inputs"""
        condition = "exists(IncomeItem[type='Employment'])"
        predicate = policy_loader._compile_condition(condition)

        # Call multiple times with same inputs
        result1 = predicate("T-001", "2024-25")
        result2 = predicate("T-001", "2024-25")
        result3 = predicate("T-001", "2024-25")

        # Should be consistent
        assert result1 == result2 == result3

    def test_predicate_different_inputs(self, policy_loader):
        """Test predicates with different input combinations"""
        condition = "exists(IncomeItem[type='Employment'])"
        predicate = policy_loader._compile_condition(condition)

        # Test with different taxpayer IDs and tax years
        test_inputs = [
            ("T-001", "2024-25"),
            ("T-002", "2024-25"),
            ("T-001", "2023-24"),
            ("T-999", "2025-26"),
        ]

        for taxpayer_id, tax_year in test_inputs:
            result = predicate(taxpayer_id, tax_year)
            assert isinstance(result, bool)

    def test_edge_case_conditions(self, policy_loader):
        """Test edge cases in condition parsing"""
        edge_cases = [
            "",  # Empty string
            "   ",  # Whitespace only
            "exists()",  # Empty exists
            "exists(Entity[])",  # Empty filter
            "taxpayer_flag:",  # Empty flag
            "filing_mode:",  # Empty mode
        ]

        for condition in edge_cases:
            predicate = policy_loader._compile_condition(condition)
            assert callable(predicate)
            # Should default to False for malformed conditions
            result = predicate("T-001", "2024-25")
            assert result is False
