"""Tests for Contract Upgrade Monitor (contract_upgrade_monitor.py)."""

from __future__ import annotations

import pytest

from app.contract_upgrade_monitor import (
    ContractUpgradeAnalyzer,
    ContractUpgradeReport,
    ProxyInfo,
    UpgradeEvent,
    detect_proxy,
    format_upgrade_report,
    is_valid_evm_address,
)


class TestAddressValidation:
    """Test EVM address validation."""

    def test_valid_address(self):
        assert is_valid_evm_address("0xdAC17F958D2ee523a2206206994597C13D831ec7")
        assert is_valid_evm_address("0x0000000000000000000000000000000000000000")

    def test_invalid_address(self):
        assert not is_valid_evm_address("")
        assert not is_valid_evm_address("not_an_address")
        assert not is_valid_evm_address("0xshort")
        assert not is_valid_evm_address("0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG")  # non-hex

    def test_checksum_case_insensitive(self):
        # Lowercase is valid (EVM addresses are case-insensitive for storage)
        assert is_valid_evm_address("0xdac17f958d2ee523a2206206994597c13d831ec7")


class TestProxyDetection:
    """Test proxy detection logic (mocked storage)."""

    @pytest.mark.asyncio
    async def test_non_proxy_returns_false(self):
        """Check that a non-proxy address returns is_proxy=False."""
        # Use a well-known EOA (Vitalik's address) - should not be a proxy
        result = await detect_proxy("0xab5801a7d398352b5532b8a7c0c8e9c6c5c9f6d5", "ethereum")
        # This might still return proxy=False or proxy with low confidence
        # depending on whether the RPC is available
        assert result is not None
        assert isinstance(result, ProxyInfo)

    def test_proxy_info_creation(self):
        info = ProxyInfo(address="0x1234", chain="ethereum")
        assert info.address == "0x1234"
        assert info.chain == "ethereum"
        assert not info.is_proxy
        assert info.confidence == 0.0

    def test_proxy_info_with_implementation(self):
        info = ProxyInfo(
            address="0x1234",
            chain="ethereum",
            implementation_address="0x5678",
            is_proxy=True,
            proxy_type="eip1967",
            proxy_type_name="EIP-1967 Transparent/Universal Proxy",
            confidence=0.9,
        )
        assert info.is_proxy
        assert info.proxy_type == "eip1967"
        assert info.implementation_address == "0x5678"


class TestUpgradeEvent:
    """Test upgrade event creation and sorting."""

    def test_upgrade_event_creation(self):
        event = UpgradeEvent(
            block_number=100,
            transaction_hash="0xabc",
            timestamp=1000000,
            previous_implementation="0xold",
            new_implementation="0xnew",
            triggered_by="0xadmin",
        )
        assert event.block_number == 100
        assert event.new_implementation == "0xnew"
        assert event.triggered_by == "0xadmin"

    def test_upgrade_event_minimal(self):
        event = UpgradeEvent(
            block_number=0,
            transaction_hash="",
            timestamp=0,
        )
        assert event.previous_implementation is None
        assert event.triggered_by is None


class TestFormatter:
    """Test report formatting."""

    def test_format_proxy_report(self):
        proxy = ProxyInfo(
            address="0xdAC17F958D2ee523a2206206994597C13D831ec7",
            chain="ethereum",
            implementation_address="0x1111111111111111111111111111111111111111",
            admin_address="0x2222222222222222222222222222222222222222",
            is_proxy=True,
            proxy_type="eip1967",
            proxy_type_name="EIP-1967 Transparent/Universal Proxy",
            confidence=0.9,
        )
        report = ContractUpgradeReport(
            contract_address="0xdAC17F958D2ee523a2206206994597C13D831ec7",
            chain="ethereum",
            proxy_info=proxy,
            timelock_status="no_timelock",
            upgrade_count_30d=2,
            upgrade_history=[
                UpgradeEvent(
                    block_number=100,
                    transaction_hash="0xabc123def456",
                    timestamp=1000000,
                ),
            ],
            recent_suspicious_upgrades=[],
            risk_score=35.0,
            risk_factors=[
                "No timelock detected - upgrades can be instant",
                "Moderate upgrade frequency: 2 upgrades in 30 days",
            ],
            admin_privileges=["upgradeTo(address)", "changeAdmin(address)"],
            summary="Summary text here",
        )
        text = format_upgrade_report(report)
        assert "CONTRACT UPGRADE MONITOR REPORT" in text
        assert "EIP-1967" in text
        assert "35.0/100" in text or "35/100" in text
        assert "Proxy Detected" in text

    def test_format_non_proxy_report(self):
        report = ContractUpgradeReport(
            contract_address="0xdAC17F958D2ee523a2206206994597C13D831ec7",
            chain="ethereum",
            proxy_info=ProxyInfo(address="0xtest", chain="ethereum"),
            risk_score=0.0,
            summary="Not a proxy",
        )
        text = format_upgrade_report(report)
        assert "not a proxy" in text.lower() or "Not a proxy" in text


class TestRiskAssessment:
    """Test risk scoring logic (internal)."""

    def test_high_risk_frequent_upgrades(self):
        from app.contract_upgrade_monitor import _assess_risk

        proxy = ProxyInfo(
            address="0xtest",
            chain="ethereum",
            is_proxy=True,
            proxy_type="eip1967",
            confidence=0.9,
        )
        upgrades = [
            UpgradeEvent(block_number=i, transaction_hash=f"0x{i}", timestamp=1000000 + i * 1000) for i in range(10)
        ]
        current_time = 1000000 + 5 * 3600  # 5 hours after first upgrade
        risk, factors = _assess_risk(proxy, upgrades, current_time)
        assert risk > 0
        assert len(factors) > 0
        # Should flag the no-timelock risk
        assert any("timelock" in f.lower() for f in factors)

    def test_low_risk_stable_proxy(self):
        from app.contract_upgrade_monitor import _assess_risk

        proxy = ProxyInfo(
            address="0xtest",
            chain="ethereum",
            is_proxy=True,
            proxy_type="eip1967",
            confidence=0.9,
        )
        risk, factors = _assess_risk(proxy, [], 2000000)
        assert risk >= 0
        # Should still flag no timelock
        assert any("timelock" in f.lower() for f in factors)

    def test_beacon_proxy_extra_risk(self):
        from app.contract_upgrade_monitor import _assess_risk

        proxy = ProxyInfo(
            address="0xtest",
            chain="ethereum",
            is_proxy=True,
            proxy_type="beacon",
            confidence=0.9,
        )
        risk, factors = _assess_risk(proxy, [], 2000000)
        assert risk >= 15  # Beacon risk penalty
        assert any("Beacon" in f for f in factors)


class TestAnalyzer:
    """Test the ContractUpgradeAnalyzer (integration-light)."""

    @pytest.mark.asyncio
    async def test_analyzer_returns_report(self):
        analyzer = ContractUpgradeAnalyzer()
        # Use a known address - USDT contract on Ethereum
        # This is a real contract but we don't rely on it being a proxy
        report = await analyzer.analyze(
            contract_address="0xdAC17F958D2ee523a2206206994597C13D831ec7",
            chain="ethereum",
        )
        assert isinstance(report, ContractUpgradeReport)
        assert report.contract_address.lower() == "0xdAC17F958D2ee523a2206206994597C13D831ec7".lower()
        assert report.chain == "ethereum"

    @pytest.mark.asyncio
    async def test_analyzer_invalid_address(self):
        analyzer = ContractUpgradeAnalyzer()
        with pytest.raises(ValueError, match="Invalid EVM address"):
            await analyzer.analyze(
                contract_address="not_an_address",
                chain="ethereum",
            )


class TestModuleFunctions:
    """Test module-level helper functions."""

    def test_get_upgrade_analyzer(self):
        from app.contract_upgrade_monitor import get_upgrade_analyzer

        instance = get_upgrade_analyzer()
        assert isinstance(instance, ContractUpgradeAnalyzer)

        # Singleton behavior
        instance2 = get_upgrade_analyzer()
        assert instance is instance2


class TestDangerousSelectors:
    """Test that dangerous function selectors are properly mapped."""

    def test_known_selectors(self):
        from app.contract_upgrade_monitor import DANGEROUS_SELECTORS

        # upgradeTo selector should be known
        assert "0x3659cfe6" in DANGEROUS_SELECTORS
        assert "upgrade" in DANGEROUS_SELECTORS["0x3659cfe6"].lower()

    def test_proxy_patterns_listed(self):
        from app.contract_upgrade_monitor import PROXY_PATTERNS

        assert "eip1967" in PROXY_PATTERNS
        assert "eip1822" in PROXY_PATTERNS
        assert "beacon" in PROXY_PATTERNS
