from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from enum import Enum, IntEnum
from typing import TypedDict

COMMAND_SERVICE_UUID = "0000fe24-0000-1000-8000-00805f9b34fb"
WRITE_CHARACTERISTIC = "bd4ac611-0b45-11e3-8ffd-0800200c9a66"
READ_CHARACTERISTIC = "bd4ac612-0b45-11e3-8ffd-0800200c9a66"
SECURE_WRITE_CHARACTERISTIC = "bd4ac613-0b45-11e3-8ffd-0800200c9a66"
SECURE_READ_CHARACTERISTIC = "bd4ac614-0b45-11e3-8ffd-0800200c9a66"

APPLE_MFR_ID = 76
YALE_MFR_ID = 465
HAP_FIRST_BYTE = 0x06
HAP_ENCRYPTED_FIRST_BYTE = 0x11


MANUFACTURER_NAME_CHARACTERISTIC = "00002a29-0000-1000-8000-00805f9b34fb"
MODEL_NUMBER_CHARACTERISTIC = "00002a24-0000-1000-8000-00805f9b34fb"
SERIAL_NUMBER_CHARACTERISTIC = "00002a25-0000-1000-8000-00805f9b34fb"
FIRMWARE_REVISION_CHARACTERISTIC = "00002a26-0000-1000-8000-00805f9b34fb"

NO_DOOR_SENSE_MODELS = {"ASL-02", "ASL-01"}


class Commands(IntEnum):
    GETSTATUS = 0x02
    WRITESETTING = 0x03
    READSETTING = 0x04
    UNLOCK = 0x0A
    LOCK = 0x0B
    LOCK_ACTIVITY = 0x2D


class StatusType(IntEnum):
    LOCK_ONLY = 0x02
    DOOR_ONLY = 0x2E
    DOOR_AND_LOCK = 0x2F
    BATTERY = 0x0F


class SettingType(IntEnum):
    AUTOLOCK = 0x28


class LockStatus(Enum):
    UNKNOWN = 0x00
    UNKNOWN_01 = 0x01  # Calibrating
    UNLOCKING = 0x02
    UNLOCKED = 0x03
    LOCKING = 0x04
    LOCKED = 0x05
    UNKNOWN_06 = 0x06  # PolDiscovery
    # STATICPOSITION = 0x07
    # UNLATCHING = 0x09
    # UNLATCHED = 0x0A
    SECUREMODE = 0x0C


VALUE_TO_LOCK_STATUS = {status.value: status for status in LockStatus}


class DoorStatus(Enum):
    UNKNOWN = 0x00  # Init
    CLOSED = 0x01
    AJAR = 0x02
    OPENED = 0x03
    UNKNOWN_04 = 0x04  # Unknown


VALUE_TO_DOOR_STATUS = {status.value: status for status in DoorStatus}


class AutoLockMode(IntEnum):
    INSTANT = 0x00
    TIMER = 0x5A
    # Not a valid value from the lock, but used to signal that auto lock is disabled
    OFF = 0xFF


VALUE_TO_AUTO_LOCK_MODE = {status.value: status for status in AutoLockMode}


class LockActivityType(Enum):
    LOCK = 0x00
    DOOR = 0x20
    PIN = 0x0E
    NONE = 0x80


@dataclass
class BatteryState:
    voltage: float
    percentage: int


@dataclass
class AutoLockState:
    mode: AutoLockMode
    duration: int


@dataclass
class LockState:
    lock: LockStatus
    door: DoorStatus
    battery: BatteryState | None
    auth: AuthState | None
    auto_lock: AutoLockState | None
    # Hold the previous auto lock state so that it can be restored if auto lock
    # is enabled
    auto_lock_prev: AutoLockState | None


LockStateValue = LockStatus | DoorStatus | BatteryState | AutoLockState


class LockOperationSource(Enum):
    REMOTE = 0x00
    MANUAL = 0x01
    AUTO_LOCK = 0x05
    PIN = 0x0B
    UNKNOWN = 0xFF


VALUE_TO_LOCK_OPERATION_SOURCE = {
    status.value: status for status in LockOperationSource
}


class LockOperationRemoteType(Enum):
    UNKNOWN = 0x00
    BLE = 0x03


VALUE_TO_LOCK_OPERATION_REMOTE_TYPE = {
    status.value: status for status in LockOperationRemoteType
}


@dataclass
class LockActivity:
    timestamp: datetime
    status: LockStatus
    source: LockOperationSource
    remote_type: LockOperationRemoteType | None = None
    slot: int | None = None


@dataclass
class DoorActivity:
    timestamp: datetime
    status: DoorStatus


@dataclass
class AuthState:
    successful: bool


@dataclass
class LockInfo:
    manufacturer: str
    model: str
    serial: str
    firmware: str

    @property
    def door_sense(self) -> bool:
        """Check if the lock has door sense support."""
        return bool(
            self.model
            and not any(
                self.model.startswith(old_model) for old_model in NO_DOOR_SENSE_MODELS
            )
        )


@dataclass
class ConnectionInfo:
    rssi: int


class YaleXSBLEDiscovery(TypedDict):
    """A validated discovery of a Yale XS BLE device."""

    name: str
    address: str
    serial: str
    key: str
    slot: int
