"""End-to-end test using mocked transport to verify full API surface."""
import struct
import asyncio
from unittest.mock import AsyncMock, MagicMock
from pm3py import Proxmark3, Cmd
from pm3py.client import FirmwareInfo


def test_full_api_surface():
    """Verify all nested attributes exist."""
    pm3 = Proxmark3("/dev/null")

    assert hasattr(pm3, "hw")
    assert hasattr(pm3, "lf")
    assert hasattr(pm3, "hf")
    assert hasattr(pm3.lf, "t55")
    assert hasattr(pm3.hf, "a14")
    assert hasattr(pm3.hf, "iso15")
    assert hasattr(pm3.hf, "mf")
    assert hasattr(pm3, "send_ng")
    assert hasattr(pm3, "send_mix")
    assert hasattr(pm3, "firmware")
    assert isinstance(pm3.firmware, FirmwareInfo)


def test_hw_ping_roundtrip():
    """Test ping returns structured dict."""
    pm3 = Proxmark3("/dev/null")
    mock_transport = AsyncMock()
    from pm3py.transport import PM3Response
    ping_data = bytes(range(32))
    mock_transport.send_ng.return_value = PM3Response(
        cmd=Cmd.PING, status=0, reason=0, ng=True, data=ping_data)
    pm3.hw._t = mock_transport
    result = asyncio.get_event_loop().run_until_complete(pm3.hw.ping(32))
    assert isinstance(result, dict)
    assert result["success"] is True


def test_mf_rdbl_returns_hex():
    """Verify MIFARE read returns hex string data."""
    pm3 = Proxmark3("/dev/null")
    mock_transport = AsyncMock()
    from pm3py.transport import PM3Response
    block = bytes(range(16))
    mock_transport.send_ng.return_value = PM3Response(
        cmd=Cmd.HF_MIFARE_READBL, status=0, reason=0, ng=True, data=block)
    pm3.hf.mf._t = mock_transport
    result = asyncio.get_event_loop().run_until_complete(
        pm3.hf.mf.rdbl(block=4, key="FFFFFFFFFFFF"))
    assert result["data"] == "000102030405060708090a0b0c0d0e0f"
    assert result["raw"] == block


def test_firmware_probe_compatible():
    """Verify firmware probe populates FirmwareInfo on successful connect."""
    pm3 = Proxmark3("/dev/null")
    mock_transport = AsyncMock()
    pm3._transport = mock_transport
    pm3.hw._t = mock_transport

    from pm3py.transport import PM3Response

    ping_data = bytes(range(32))
    vstr = b"Proxmark3 RDV4.0 FW v4.18000\x00"
    version_payload = struct.pack("<III", 0x270B0A40, 512*1024, len(vstr)) + vstr

    # send_ng called twice: once for ping, once for version
    mock_transport.send_ng.side_effect = [
        PM3Response(cmd=Cmd.PING, status=0, reason=0, ng=True, data=ping_data),
        PM3Response(cmd=Cmd.VERSION, status=0, reason=0, ng=True, data=version_payload),
    ]

    asyncio.get_event_loop().run_until_complete(pm3._probe_firmware())
    assert pm3.firmware.compatible is True
    assert pm3.firmware.ng_protocol is True
    assert "Proxmark3 RDV4.0" in pm3.firmware.version_string
    assert pm3.firmware.warnings == []


def test_firmware_probe_ping_fails():
    """Verify firmware probe marks incompatible when ping fails."""
    pm3 = Proxmark3("/dev/null")
    mock_transport = AsyncMock()
    pm3._transport = mock_transport
    pm3.hw._t = mock_transport

    from pm3py.transport import PM3Response, PM3Error

    vstr = b"Proxmark3 OLD\x00"
    version_payload = struct.pack("<III", 0x270B0A40, 256*1024, len(vstr)) + vstr

    mock_transport.send_ng.side_effect = [
        PM3Error("Ping failed"),
        PM3Response(cmd=Cmd.VERSION, status=0, reason=0, ng=True, data=version_payload),
    ]

    asyncio.get_event_loop().run_until_complete(pm3._probe_firmware())
    assert pm3.firmware.compatible is False
    assert pm3.firmware.ng_protocol is False
    assert len(pm3.firmware.warnings) > 0
