"""Main Proxmark3 client with nested API."""
import logging
from dataclasses import dataclass, field

from .transport import PM3Transport, PM3Response, PM3Error, ProgressCallback
from .hw import HWCommands
from .lf import LFCommands
from .hf import HFCommands
from .hf_14a import HF14ACommands
from .hf_15 import HF15Commands
from .hf_mf import HFMFCommands

log = logging.getLogger(__name__)


@dataclass
class FirmwareInfo:
    """Firmware version and compatibility information populated on connect."""
    version_string: str = ""
    chip_id: str = ""
    section_size: int = 0
    ng_protocol: bool = False
    compatible: bool = False
    warnings: list[str] = field(default_factory=list)


class Proxmark3:
    """Proxmark3 device client with structured Python API.

    Usage:
        async with Proxmark3("/dev/ttyACM0") as pm3:
            if not pm3.firmware.compatible:
                print("Warnings:", pm3.firmware.warnings)
            status = await pm3.hw.version()
            print(status)

            # Raw access:
            resp = await pm3.send_ng(0x0108)
    """

    def __init__(self, port: str, baudrate: int = 115200):
        self._transport = PM3Transport(port, baudrate)
        self.hw = HWCommands(self._transport)
        self.lf = LFCommands(self._transport)
        self.hf = HFCommands(self._transport)
        self.hf.a14 = HF14ACommands(self._transport)
        self.hf.iso15 = HF15Commands(self._transport)
        self.hf.mf = HFMFCommands(self._transport)
        self.firmware = FirmwareInfo()

    async def connect(self) -> None:
        await self._transport.connect()
        await self._probe_firmware()

    async def disconnect(self) -> None:
        await self._transport.disconnect()

    async def __aenter__(self) -> "Proxmark3":
        await self.connect()
        return self

    async def __aexit__(self, *args) -> None:
        await self.disconnect()

    async def _probe_firmware(self) -> None:
        """Ping device and fetch firmware version after connecting."""
        fw = self.firmware
        fw.warnings = []

        # Step 1: Ping to verify NG protocol support
        try:
            ping_result = await self.hw.ping(32)
            fw.ng_protocol = ping_result.get("success", False)
            if not fw.ng_protocol:
                fw.warnings.append("Ping echo mismatch — device may not support NG protocol")
        except PM3Error:
            fw.ng_protocol = False
            fw.warnings.append("Ping failed — device may not support NG protocol or may be in bootloader mode")

        # Step 2: Fetch firmware version
        try:
            ver = await self.hw.version()
            fw.version_string = ver.get("version_string", "")
            fw.chip_id = ver.get("chip_id", "")
            fw.section_size = ver.get("section_size", 0)
        except PM3Error:
            fw.warnings.append("Could not read firmware version — device may be in bootloader mode")

        # Step 3: Assess compatibility
        fw.compatible = fw.ng_protocol and len(fw.warnings) == 0

        for w in fw.warnings:
            log.warning("pm3py: %s", w)

    async def send_ng(self, cmd: int, payload: bytes = b"",
                      timeout: float = 2.0) -> PM3Response:
        """Raw NG command -- escape hatch for unwrapped commands."""
        return await self._transport.send_ng(cmd, payload, timeout)

    async def send_mix(self, cmd: int, arg0: int = 0, arg1: int = 0,
                       arg2: int = 0, payload: bytes = b"",
                       timeout: float = 2.0) -> PM3Response:
        """Raw MIX command -- escape hatch for unwrapped commands."""
        return await self._transport.send_mix(cmd, arg0, arg1, arg2, payload, timeout)
