"""ISO 15693 commands: hf.15.*"""
import struct
from .protocol import Cmd
from .transport import PM3Transport, PM3Error

# ISO15 PM3 flags
ISO15_CONNECT = 0x01
ISO15_NO_DISCONNECT = 0x02
ISO15_RAW = 0x04
ISO15_APPEND_CRC = 0x08
ISO15_READ_RESPONSE = 0x10


class HF15Commands:
    """ISO 15693 commands."""

    def __init__(self, transport: PM3Transport):
        self._t = transport

    async def scan(self) -> dict:
        """Scan for ISO15693 tags using inventory command."""
        # ISO15693 inventory: flags=0x06 (high data rate + inventory), cmd=0x01
        iso_cmd = bytes([0x06, 0x01, 0x00])  # flags, inventory cmd, mask_len=0
        flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
        payload = struct.pack("<BH", flags, len(iso_cmd)) + iso_cmd
        resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0)

        if resp.status != 0 or len(resp.data) < 10:
            return {"found": False, "uid": None}

        resp_flags = resp.data[0]
        dsfid = resp.data[1]
        uid = resp.data[2:10]

        return {
            "found": True,
            "uid": uid[::-1].hex(),  # ISO15693 UIDs are reversed
            "dsfid": dsfid,
            "response_flags": resp_flags,
        }

    async def rdbl(self, block: int, uid: str | None = None) -> dict:
        """Read a single block."""
        if uid:
            uid_bytes = bytes.fromhex(uid)[::-1]  # reverse for wire
            iso_cmd = bytes([0x22, 0x20]) + uid_bytes + bytes([block])
        else:
            iso_cmd = bytes([0x02, 0x20, block])  # addressed without UID

        flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
        payload = struct.pack("<BH", flags, len(iso_cmd)) + iso_cmd
        resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0)

        if resp.status != 0 or len(resp.data) < 2:
            return {"success": False, "error": resp.status}

        resp_flags = resp.data[0]
        if resp_flags != 0:
            return {"success": False, "error_flag": resp_flags}

        lock_status = resp.data[1]
        block_data = resp.data[2:]

        return {
            "success": True,
            "block": block,
            "locked": bool(lock_status),
            "data": block_data.hex(),
            "raw": block_data,
        }

    async def wrbl(self, block: int, data: bytes | str, uid: str | None = None) -> dict:
        """Write a single block."""
        if isinstance(data, str):
            data = bytes.fromhex(data)

        if uid:
            uid_bytes = bytes.fromhex(uid)[::-1]
            iso_cmd = bytes([0x22, 0x21]) + uid_bytes + bytes([block]) + data
        else:
            iso_cmd = bytes([0x02, 0x21, block]) + data

        flags = ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_APPEND_CRC
        payload = struct.pack("<BH", flags, len(iso_cmd)) + iso_cmd
        resp = await self._t.send_ng(Cmd.HF_ISO15693_COMMAND, payload, timeout=5.0)

        success = resp.status == 0 and len(resp.data) > 0 and resp.data[0] == 0
        return {"success": success, "block": block}

    async def sniff(self) -> dict:
        """Sniff ISO15693 traffic."""
        resp = await self._t.send_ng(Cmd.HF_ISO15693_SNIFF, timeout=30.0)
        return {"status": resp.status}
