"""Keypad Vision (Pro) device handling."""

import logging
import re
from typing import Any

from bleak.backends.device import BLEDevice

from ..const import SwitchbotModel
from .device import SwitchbotEncryptedDevice, SwitchbotSequenceDevice

PASSWORD_RE = re.compile(r"^\d{6,12}$")
COMMAND_GET_PASSWORD_COUNT = "570F530100"

_LOGGER = logging.getLogger(__name__)


class SwitchbotKeypadVision(SwitchbotSequenceDevice, SwitchbotEncryptedDevice):
    """Representation of a Switchbot Keypad Vision (Pro) device."""

    def __init__(
        self,
        device: BLEDevice,
        key_id: str,
        encryption_key: str,
        model: SwitchbotModel,
        **kwargs: Any,
    ) -> None:
        """Initialize Keypad Vision (Pro) device."""
        super().__init__(device, key_id, encryption_key, model=model, **kwargs)

    @classmethod
    async def verify_encryption_key(
        cls,
        device: BLEDevice,
        key_id: str,
        encryption_key: str,
        model: SwitchbotModel,
        **kwargs: Any,
    ) -> bool:
        return await super().verify_encryption_key(
            device, key_id, encryption_key, model, **kwargs
        )

    async def get_basic_info(self) -> dict[str, Any] | None:
        """Get device basic settings."""
        if not (_data := await self._get_basic_info()):
            return None
        _LOGGER.debug("Raw model %s basic info data: %s", self._model, _data.hex())

        battery = _data[1] & 0x7F
        firmware = _data[2] / 10.0
        hardware = _data[3]
        support_fingerprint = _data[4]
        lock_button_enabled = bool(_data[5] != 1)
        tamper_alarm_enabled = bool(_data[9])
        backlight_enabled = bool(_data[10] != 1)
        backlight_level = _data[11]
        prompt_tone_enabled = bool(_data[12] != 1)

        if self._model == SwitchbotModel.KEYPAD_VISION:
            battery_charging = bool((_data[14] & 0x06) >> 1)
        else:
            battery_charging = bool((_data[14] & 0x0E) >> 1)

        result = {
            "battery": battery,
            "firmware": firmware,
            "hardware": hardware,
            "support_fingerprint": support_fingerprint,
            "lock_button_enabled": lock_button_enabled,
            "tamper_alarm_enabled": tamper_alarm_enabled,
            "backlight_enabled": backlight_enabled,
            "backlight_level": backlight_level,
            "prompt_tone_enabled": prompt_tone_enabled,
            "battery_charging": battery_charging,
        }

        _LOGGER.debug("%s basic info: %s", self._model, result)
        return result

    def _check_password_rules(self, password: str) -> None:
        """Check if the password compliant with the rules."""
        if not PASSWORD_RE.fullmatch(password):
            raise ValueError("Password must be 6-12 digits.")

    def _build_password_payload(self, password: str) -> bytes:
        """Build password payload."""
        pwd_bytes = bytes(int(ch) for ch in password)
        pwd_length = len(pwd_bytes)

        payload = bytearray()
        payload.append(0xFF)
        payload.append(0x00)
        payload.append(pwd_length)
        payload.extend(pwd_bytes)

        return bytes(payload)

    def _build_add_password_cmd(self, password: str) -> list[str]:
        """Build command to add a password."""
        cmd_header = bytes.fromhex("570F520202")

        payload = self._build_password_payload(password)

        max_payload = 11

        chunks = [
            payload[i : i + max_payload] for i in range(0, len(payload), max_payload)
        ]
        total = len(chunks)
        cmds: list[str] = []

        for idx, chunk in enumerate(chunks):
            packet_info = ((total & 0x0F) << 4) | (idx & 0x0F)

            cmd = bytearray()
            cmd.extend(cmd_header)
            cmd.append(packet_info)
            cmd.extend(chunk)

            cmds.append(cmd.hex().upper())

        _LOGGER.debug(
            "device: %s add password commands: %s", self._device.address, cmds
        )

        return cmds

    async def add_password(self, password: str) -> bool:
        """Add a password to the Keypad Vision (Pro)."""
        self._check_password_rules(password)
        cmds = self._build_add_password_cmd(password)
        return await self._send_command_sequence(cmds)

    async def get_password_count(self) -> dict[str, int] | None:
        """Get the number of passwords stored in the Keypad Vision (Pro)."""
        if not (_data := await self._send_command(COMMAND_GET_PASSWORD_COUNT)):
            return None
        _LOGGER.debug("Raw model %s password count data: %s", self._model, _data.hex())

        pin = _data[1]
        nfc = _data[2]
        fingerprint = _data[3]
        duress_pin = _data[4]
        duress_fingerprint = _data[5]

        result = {
            "pin": pin,
            "nfc": nfc,
            "fingerprint": fingerprint,
            "duress_pin": duress_pin,
            "duress_fingerprint": duress_fingerprint,
        }

        if self._model == SwitchbotModel.KEYPAD_VISION_PRO:
            face = _data[6]
            palm_vein = _data[7]
            result.update(
                {
                    "face": face,
                    "palm_vein": palm_vein,
                }
            )

        _LOGGER.debug("%s password count: %s", self._model, result)
        return result
