from typing import List, Dict, Any
from .base_tests import BaseTestGenerator

class FloatTestGenerator(BaseTestGenerator):
    """Generate test cases for floating point literals."""

    # Special float values
    SPECIAL_VALUES = {
        'f32': {
            'min': -3.4028235e38,
            'max': 3.4028235e38,
            'min_positive': 1.1754944e-38,
            'epsilon': 1.1920929e-7,
        },
        'f64': {
            'min': -1.7976931348623157e308,
            'max': 1.7976931348623157e308,
            'min_positive': 2.2250738585072014e-308,
            'epsilon': 2.220446049250313e-16,
        }
    }

    def generate_basic_tests(self):
        """Generate basic test cases."""
        # Simple default floats (f64)
        self.make_success_test("Float Default Simple", "3.14", "f64", 3.14)
        self.make_success_test("Float Default Zero", "0.0", "f64", 0.0)
        self.make_success_test("Float Default Negative", "-2.5", "f64", -2.5)
        self.make_success_test("Float Default One", "1.0", "f64", 1.0)

        # Simple with type annotation
        self.make_success_test("Float f32 Simple", "3.14:f32", "f32", 3.14)
        self.make_success_test("Float f64 Simple", "2.718:f64", "f64", 2.718)

    def generate_format_tests(self):
        """Generate tests for different float formats."""
        # Leading zeros
        self.make_success_test("Float Default Leading Zeros", "00042.5", "f64", 42.5)
        self.make_success_test("Float Default Leading Zero Decimal", "0.5", "f64", 0.5)

        # Trailing zeros
        self.make_success_test("Float Default Trailing Zeros", "3.1400", "f64", 3.14)

        # No leading digit
        self.make_success_test("Float Default No Leading Digit", ".5", "f64", 0.5)
        self.make_success_test("Float Default No Leading Digit Negative", "-.25", "f64", -0.25)

        # No trailing digits
        self.make_success_test("Float Default No Trailing Digits", "42.", "f64", 42.0)
        self.make_success_test("Float Default No Trailing Digits Negative", "-7.", "f64", -7.0)

        if self.ENABLE_EXPONENTIAL_LITERALS:
            # Scientific notation
            self.make_success_test("Float Default Scientific Positive Exp", "1.5e10", "f64", 1.5e10)
            self.make_success_test("Float Default Scientific Negative Exp", "2.5e-5", "f64", 2.5e-5)
            self.make_success_test("Float Default Scientific Capital E", "3.14E8", "f64", 3.14e8)
            self.make_success_test("Float Default Scientific Plus Sign", "1.0e+3", "f64", 1000.0)

        # Very small numbers
        self.make_success_test("Float Default Very Small", "0.000001", "f64", 0.000001)
        if self.ENABLE_EXPONENTIAL_LITERALS:
            self.make_success_test("Float Default Scientific Very Small", "1.0e-20", "f64", 1.0e-20)

        # Very large numbers
        self.make_success_test("Float Default Very Large", "1000000.0", "f64", 1000000.0)
        if self.ENABLE_EXPONENTIAL_LITERALS:
            self.make_success_test("Float Default Scientific Very Large", "1.0e20", "f64", 1.0e20)

    def generate_underscore_tests(self):
        """Generate tests for underscores in floats."""
        # Underscores in integer part
        self.make_success_test("Float Default Underscore Integer Part",
                              "1_000_000.5", "f64", 1000000.5)

        # Underscores in decimal part
        self.make_success_test("Float Default Underscore Decimal Part",
                              "3.141_592_653", "f64", 3.141592653)

        # Underscores in both parts
        self.make_success_test("Float Default Underscore Both Parts",
                              "1_234.567_89", "f64", 1234.56789)

        if self.ENABLE_EXPONENTIAL_LITERALS:
            # Underscores in scientific notation
            self.make_success_test("Float Default Underscore Scientific Mantissa",
                                "1_000.5e10", "f64", 1000.5e10)
            self.make_success_test("Float Default Underscore Scientific Exponent",
                                "1.5e1_0", "f64", 1.5e10)

        # Trailing underscore
        self.make_success_test("Float Default Underscore Trailing", "42.5_", "f64", 42.5)

        # Double underscore
        self.make_success_test("Float Default Underscore Double", "4__2.5", "f64", 42.5)

        # With type annotation
        self.make_success_test("Float f32 With Underscores",
                              "1_234.567_89:f32", "f32", 1234.56789)

    def generate_special_value_tests(self):
        """Generate tests for special float values."""
        # Infinity
        self.make_success_test("Float Default Positive Infinity", "inf", "f64", float('inf'))
        self.make_success_test("Float Default Negative Infinity", "-inf", "f64", float('-inf'))
        self.make_success_test("Float f32 Positive Infinity", "inf:f32", "f32", float('inf'))
        self.make_success_test("Float f32 Negative Infinity", "-inf:f32", "f32", float('-inf'))

        # NaN
        self.make_success_test("Float Default NaN", "nan", "f64", float('nan'))
        self.make_success_test("Float f32 NaN", "nan:f32", "f32", float('nan'))

        # Note: NaN comparison is special - NaN != NaN, so these tests may need
        # special handling in the test runner

    def generate_edge_case_tests(self, type_name: str):
        """Generate edge case tests for a specific float type."""
        values = self.SPECIAL_VALUES[type_name]

        if self.ENABLE_EXPONENTIAL_LITERALS:
            # Maximum value
            self.make_success_test(f"Float {type_name} Max Value",
                                f"{values['max']}:{type_name}", type_name, values['max'])

            # Minimum value (most negative)
            self.make_success_test(f"Float {type_name} Min Value",
                                f"{values['min']}:{type_name}", type_name, values['min'])

            # Smallest positive normalized value
            self.make_success_test(f"Float {type_name} Min Positive",
                                f"{values['min_positive']}:{type_name}",
                                type_name, values['min_positive'])

            # Machine epsilon
            self.make_success_test(f"Float {type_name} Epsilon",
                                f"{values['epsilon']}:{type_name}",
                                type_name, values['epsilon'])

            # Near zero
            self.make_success_test(f"Float {type_name} Near Zero Positive",
                                f"1e-30:{type_name}", type_name, 1e-30)
            self.make_success_test(f"Float {type_name} Near Zero Negative",
                                f"-1e-30:{type_name}", type_name, -1e-30)

            # Subnormal numbers
            if type_name == 'f64':
                self.make_success_test("Float f64 Subnormal",
                                    "1e-320:f64", "f64", 1e-320)
            elif type_name == 'f32':
                self.make_success_test("Float f32 Subnormal",
                                    "1e-40:f32", "f32", 1e-40)

    def generate_overflow_tests(self):
        """Generate overflow tests."""
        if self.ENABLE_EXPONENTIAL_LITERALS:
            # f32 overflow
            self.make_error_test("Float f32 Overflow Positive",
                            "1e40:f32",
                            "Float overflow: value exceeds range for f32.")
            self.make_error_test("Float f32 Overflow Negative",
                            "-1e40:f32",
                            "Float overflow: value exceeds range for f32.")

            # f64 overflow (extremely large values)
            self.make_error_test("Float f64 Overflow Positive",
                            "1e310:f64",
                            "Float overflow: value exceeds range for f64.")
            self.make_error_test("Float f64 Overflow Negative",
                            "-1e310:f64",
                            "Float overflow: value exceeds range for f64.")

    def generate_precision_tests(self):
        """Generate tests for precision limits."""
        # f32 precision (~7 decimal digits)
        self.make_success_test("Float f32 Precision Limit",
                              "1.2345678:f32", "f32", 1.2345678)
        self.make_success_test("Float f32 High Precision",
                              "3.141592653589793:f32", "f32", 3.141592653589793)

        # f64 precision (~15 decimal digits)
        self.make_success_test("Float f64 Precision Limit",
                              "1.234567890123456:f64", "f64", 1.234567890123456)
        self.make_success_test("Float f64 High Precision",
                              "3.141592653589793238:f64", "f64", 3.141592653589793238)

        # Very close numbers
        self.make_success_test("Float f64 Close Numbers 1",
                              "1.0000000000000001:f64", "f64", 1.0000000000000001)
        self.make_success_test("Float f64 Close Numbers 2",
                              "1.0000000000000002:f64", "f64", 1.0000000000000002)

    def generate_error_tests(self):
        """Generate error tests."""
        # Invalid formats
        self.make_error_test("Float Invalid Multiple Decimal Points",
                           "3.14.159",
                           "Invalid float literal: unexpected '.' in float.")
        self.make_error_test("Float Invalid Characters",
                           "3.1a4",
                           "Invalid float literal: unexpected 'a' in float.")

        if self.ENABLE_EXPONENTIAL_LITERALS:
            # Invalid scientific notation
            self.make_error_test("Float Invalid Scientific No Exponent",
                            "3.14e",
                            "Invalid float literal: missing exponent value.")
            self.make_error_test("Float Invalid Scientific Double E",
                            "3.14e10e5",
                            "Invalid float literal: unexpected 'e' in float.")
            self.make_error_test("Float Invalid Scientific Invalid Exponent",
                            "3.14eX",
                            "Invalid float literal: unexpected 'X' in float.")

        # Invalid type annotations
        self.make_error_test("Float Invalid Type Annotation",
                           "3.14:i32",
                           "Invalid float type: must be of type 'f64' or 'f32'.")
        self.make_error_test("Float Invalid Type Name",
                           "3.14:f16",
                           "Invalid float type: must be of type 'f64' or 'f32'.")

        # Comma separators not allowed
        self.make_error_test("Float Invalid Comma Separator",
                           "1,234.56",
                           "Invalid decimal literal: unexpected ',' in decimal integer.")

    def generate_whitespace_tests(self):
        """Generate tests with whitespace."""
        self.make_success_test("Float Default Leading Whitespace",
                              "  3.14", "f64", 3.14)
        self.make_success_test("Float Default Trailing Whitespace",
                              "3.14  ", "f64", 3.14)
        self.make_success_test("Float Default Both Whitespace",
                              "  3.14  ", "f64", 3.14)
        self.make_success_test("Float f32 With Whitespace",
                              "  2.718:f32  ", "f32", 2.718)

    def generate_mathematical_constants_tests(self):
        """Generate tests for common mathematical constants."""
        # Pi
        self.make_success_test("Float Default Pi Approximate",
                              "3.141592653589793", "f64", 3.141592653589793)
        self.make_success_test("Float f32 Pi Approximate",
                              "3.1415927:f32", "f32", 3.1415927)

        # Euler's number
        self.make_success_test("Float Default Euler Approximate",
                              "2.718281828459045", "f64", 2.718281828459045)
        self.make_success_test("Float f32 Euler Approximate",
                              "2.7182817:f32", "f32", 2.7182817)

        # Golden ratio
        self.make_success_test("Float Default Golden Ratio",
                              "1.618033988749895", "f64", 1.618033988749895)

        # Square root of 2
        self.make_success_test("Float Default Sqrt2",
                              "1.4142135623730951", "f64", 1.4142135623730951)

    def generate_signed_zero_tests(self):
        """Generate tests for signed zeros."""
        self.make_success_test("Float Default Positive Zero", "0.0", "f64", 0.0)
        self.make_success_test("Float Default Negative Zero", "-0.0", "f64", -0.0)
        self.make_success_test("Float f32 Positive Zero", "0.0:f32", "f32", 0.0)
        self.make_success_test("Float f32 Negative Zero", "-0.0:f32", "f32", -0.0)

        # Note: In IEEE 754, +0.0 and -0.0 are distinct values but compare equal

    def generate_all_tests(self) -> List[Dict[str, Any]]:
        """Generate all test cases."""
        # Basic tests
        self.generate_basic_tests()

        # Format variations
        self.generate_format_tests()

        # Underscores
        self.generate_underscore_tests()

        # Special values (inf, nan)
        # self.generate_special_value_tests()

        # Edge cases for each type
        for type_name in ['f32', 'f64']:
            self.generate_edge_case_tests(type_name)

        # Overflow tests
        self.generate_overflow_tests()

        # Precision tests
        self.generate_precision_tests()

        # Error tests
        self.generate_error_tests()

        # Whitespace tests
        self.generate_whitespace_tests()

        # Mathematical constants
        self.generate_mathematical_constants_tests()

        # Signed zeros
        self.generate_signed_zero_tests()

        return self.tests
