from ci.util.global_interrupt_handler import handle_keyboard_interrupt


"""
Comprehensive tests for enhanced Arduino package index implementation with Pydantic

Tests all Pydantic models, validation rules, parsing functionality, and error handling.
"""

import json
from typing import Any

import pytest
from pydantic import ValidationError

# Import the enhanced implementation
from ci.compiler.packages import (
    Board,
    Help,
    PackageIndexParser,
    PackageParsingError,
    Platform,
    SystemDownload,
    format_size,
)


class TestHelp:
    """Test Help model validation"""

    def test_valid_help(self) -> None:
        """Test valid help creation"""
        help_data = {"online": "https://github.com/espressif/arduino-esp32"}
        help_obj = Help(**help_data)  # type: ignore
        assert str(help_obj.online) == "https://github.com/espressif/arduino-esp32"

    def test_invalid_url(self) -> None:
        """Test invalid URL validation"""
        with pytest.raises(ValidationError):
            Help(online="not-a-valid-url")  # type: ignore


class TestBoard:
    """Test Board model validation"""

    def test_valid_board(self) -> None:
        """Test valid board creation"""
        board_data = {
            "name": "ESP32 Dev Module",
            "properties": {
                "upload.tool": "esptool_py",
                "upload.maximum_size": "1310720",
            },
        }
        board = Board(**board_data)  # type: ignore
        assert board.name == "ESP32 Dev Module"
        assert board.properties["upload.tool"] == "esptool_py"

    def test_empty_name_validation(self) -> None:
        """Test empty name validation"""
        with pytest.raises(ValidationError):
            Board(name="", properties={})

    def test_name_trimming(self) -> None:
        """Test name trimming functionality"""
        board = Board(name="  ESP32 Dev Module  ", properties={})
        assert board.name == "ESP32 Dev Module"


class TestSystemDownload:
    """Test SystemDownload model validation"""

    def test_size_conversion_from_string(self) -> None:
        """Test size conversion from string bytes to MB"""
        system = SystemDownload(
            host="test-host",
            url="https://example.com/file.tar.gz",  # type: ignore
            archiveFileName="file.tar.gz",
            checksum="SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
            size="52428800",  # type: ignore - 50 MB in bytes, validator converts
        )
        assert abs(system.size_mb - 50.0) < 0.1  # Should be ~50 MB

    def test_invalid_checksum_format(self) -> None:
        """Test invalid checksum format validation"""
        with pytest.raises(ValidationError):
            SystemDownload(
                host="test-host",
                url="https://example.com/file.tar.gz",  # type: ignore
                archiveFileName="file.tar.gz",
                checksum="invalid-checksum",
                size=50.0,  # type: ignore
            )


class TestPlatform:
    """Test Platform model validation"""

    def test_valid_platform(self) -> None:
        """Test valid platform creation"""
        platform_data: dict[str, Any] = {
            "name": "ESP32 Arduino",
            "architecture": "esp32",
            "version": "2.0.5",
            "category": "ESP32",
            "url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.5/esp32-2.0.5.zip",
            "archiveFileName": "esp32-2.0.5.zip",
            "checksum": "SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
            "size": "50000000",
            "boards": [],
            "toolsDependencies": [],
            "help": {"online": "https://github.com/espressif/arduino-esp32"},
        }
        platform = Platform(**platform_data)  # type: ignore
        assert platform.name == "ESP32 Arduino"
        assert platform.architecture == "esp32"
        assert platform.size_mb == 50000000 / (1024 * 1024)

    def test_invalid_archive_extension(self) -> None:
        """Test invalid archive extension validation"""
        with pytest.raises(ValidationError):
            Platform(
                name="Test Platform",
                architecture="test",
                version="1.0.0",
                category="Test",
                url="https://example.com/file.txt",  # type: ignore
                archiveFileName="file.txt",  # Invalid extension
                checksum="SHA-256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
                size=50.0,  # type: ignore
                boards=[],
                toolsDependencies=[],
                help=Help(online="https://example.com"),  # type: ignore
            )


class TestPackageIndexParser:
    """Test PackageIndexParser functionality"""

    def test_parse_valid_json(self) -> None:
        """Test parsing valid package index JSON"""
        valid_json: dict[str, Any] = {
            "packages": [
                {
                    "name": "test",
                    "maintainer": "Test Maintainer",
                    "websiteURL": "https://example.com",
                    "email": "test@example.com",
                    "help": {"online": "https://example.com"},
                    "platforms": [],
                    "tools": [],
                }
            ]
        }

        parser = PackageIndexParser()
        package_index = parser.parse_package_index(json.dumps(valid_json))
        assert len(package_index.packages) == 1
        assert package_index.packages[0].name == "test"

    def test_parse_invalid_json(self) -> None:
        """Test parsing invalid JSON"""
        parser = PackageIndexParser()

        with pytest.raises(PackageParsingError):
            parser.parse_package_index("invalid json")


class TestUtilityFunctions:
    """Test utility functions"""

    def test_format_size(self) -> None:
        """Test size formatting function"""
        # Test KB
        assert format_size(0.5) == "512.0 KB"

        # Test MB
        assert format_size(1.5) == "1.5 MB"
        assert format_size(512.0) == "512.0 MB"

        # Test GB
        assert format_size(1536.0) == "1.5 GB"
        assert format_size(2048.0) == "2.0 GB"


class TestRealDataParsing:
    """Test with real ESP32 package index data (if network available)"""

    def test_esp32_package_parsing(self) -> None:
        """Test parsing real ESP32 package index"""
        ESP32_URL = "https://espressif.github.io/arduino-esp32/package_esp32_index.json"

        try:
            parser = PackageIndexParser(timeout=10)
            package_index = parser.parse_from_url(ESP32_URL)

            # Basic validation
            assert len(package_index.packages) > 0

            esp32_package = package_index.packages[0]
            assert esp32_package.name == "esp32"
            assert len(esp32_package.platforms) > 0
            assert len(esp32_package.tools) > 0

            print("✅ Successfully parsed ESP32 package index:")
            print(f"   📦 Packages: {len(package_index.packages)}")
            print(f"   🛠️  Platforms: {len(esp32_package.platforms)}")
            print(f"   🔧 Tools: {len(esp32_package.tools)}")

        except KeyboardInterrupt as ki:
            handle_keyboard_interrupt(ki)
            raise
        except Exception as e:
            # Skip if network not available
            pytest.skip(f"Network test skipped: {e}")


if __name__ == "__main__":
    # Run tests
    pytest.main([__file__, "-v"])
