"""Lock for Yale Alarm."""

from typing import Any, override

from yalesmartalarmclient import YaleLock, YaleLockState

from homeassistant.components.lock import LockEntity, LockState
from homeassistant.const import ATTR_CODE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import YaleConfigEntry
from .const import (
    CONF_LOCK_CODE_DIGITS,
    DEFAULT_LOCK_CODE_DIGITS,
    DOMAIN,
    YALE_ALL_ERRORS,
)
from .coordinator import YaleDataUpdateCoordinator
from .entity import YaleLockEntity

LOCK_STATE_MAP = {
    YaleLockState.LOCKED: LockState.LOCKED,
    YaleLockState.UNLOCKED: LockState.UNLOCKED,
    YaleLockState.DOOR_OPEN: LockState.OPEN,
}


async def async_setup_entry(
    hass: HomeAssistant,
    entry: YaleConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the Yale lock entry."""

    coordinator = entry.runtime_data
    code_format = entry.options.get(CONF_LOCK_CODE_DIGITS, DEFAULT_LOCK_CODE_DIGITS)

    async_add_entities(
        YaleDoorlock(coordinator, lock, code_format) for lock in coordinator.locks
    )


class YaleDoorlock(YaleLockEntity, LockEntity):
    """Representation of a Yale doorlock."""

    _attr_name = None

    def __init__(
        self, coordinator: YaleDataUpdateCoordinator, lock: YaleLock, code_format: int
    ) -> None:
        """Initialize the Yale Lock Device."""
        super().__init__(coordinator, lock)
        self._attr_code_format = rf"^\d{{{code_format}}}$"

    @override
    async def async_unlock(self, **kwargs: Any) -> None:
        """Send unlock command."""
        code: str | None = kwargs.get(ATTR_CODE)
        return await self.async_set_lock(YaleLockState.UNLOCKED, code)

    @override
    async def async_lock(self, **kwargs: Any) -> None:
        """Send lock command."""
        return await self.async_set_lock(YaleLockState.LOCKED, None)

    async def async_set_lock(self, state: YaleLockState, code: str | None) -> None:
        """Set lock."""
        lock_state = False
        try:
            if state is YaleLockState.LOCKED:
                lock_state = await self.hass.async_add_executor_job(
                    self.lock_data.close
                )
            if code and state is YaleLockState.UNLOCKED:
                lock_state = await self.hass.async_add_executor_job(
                    self.lock_data.open, code
                )
        except YALE_ALL_ERRORS as error:
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="set_lock",
                translation_placeholders={
                    "name": self.lock_data.name,
                    "error": str(error),
                },
            ) from error

        if lock_state:
            self.lock_data.set_state(state)
            self.async_write_ha_state()
            return
        raise HomeAssistantError(
            translation_domain=DOMAIN,
            translation_key="could_not_change_lock",
        )

    @property
    @override
    def is_locked(self) -> bool | None:
        """Return true if the lock is locked."""
        return LOCK_STATE_MAP.get(self.lock_data.state()) == LockState.LOCKED

    @property
    @override
    def is_open(self) -> bool | None:
        """Return true if the lock is open."""
        return LOCK_STATE_MAP.get(self.lock_data.state()) == LockState.OPEN
