"""Mapping to create a CCL Device copy."""
from __future__ import annotations

import logging
import time
from typing import Callable

from .sensor import CCLSensor, CCL_SENSORS

_LOGGER = logging.getLogger(__name__)

CCL_DEVICE_INFO_TYPES = ("serial_no", "mac_address", "model", "version")

class CCLDevice:
    def __init__(self, passkey: str):
        """Initialize a CCL device."""
        _LOGGER.debug('Initializing CCL Device: %s', self)
        self._passkey = passkey
        
        self._serial_no: str | None
        self._mac_address: str | None
        self._model: str | None
        self._version: str | None
        self._binary_sensors: dict[str, CCLSensor] | None = {}
        self._sensors: dict[str, CCLSensor] | None = {}
        self._last_updated_time: float | None

        self._new_sensors: list[CCLSensor] | None = []
        
        self._update_callbacks = set() 
        self._new_sensor_callbacks = set()
    
    @property
    def passkey(self) -> str:
        return self._passkey
    
    @property
    def device_id(self) -> str | None:
        return self._mac_address.replace(":", "").lower()[-6:]
    
    @property
    def serial_no(self) -> str | None:
        return self._serial_no
    
    @property
    def mac_address(self) -> str | None:
        return self._mac_address
    
    @property
    def model(self) -> str | None:
        return self._model
    
    @property
    def version(self) -> str | None:
        return self._version
    
    @property
    def binary_sensors(self) -> dict[str, CCLSensor] | None:
        return self._binary_sensors
    
    @property
    def sensors(self) -> dict[str, CCLSensor] | None:
        return self._sensors
    
    def update_info(self, info: dict[str, None | str]) -> None:
        """Add or update device info."""
        self._serial_no = info.get('serial_no')
        self._mac_address = info.get('mac_address')
        self._model = info.get('model')
        self._version = info.get('version')
    
    def update_sensors(self, sensors: dict[str, None | str | int | float]) -> None:
        """Add or update all sensor values."""
        for key, value in sensors.items():
            if CCL_SENSORS[key].binary:
                if not self._binary_sensors.get(key):
                    self._binary_sensors[key] = CCLSensor(key)
                    self._new_sensors.append(self._binary_sensors[key])
                self._binary_sensors[key].value = value
            else:
                if not self._sensors.get(key):
                    self._sensors[key] = CCLSensor(key)
                    self._new_sensors.append(self.sensors[key])
                self._sensors[key].value = value
        self._publish_new_sensors()
        self._publish_updates()
        self._last_updated_time = time.monotonic()
        _LOGGER.debug("Sensors Updated: %s", self._last_updated_time)

    def register_update_cb(self, callback: Callable[[], None]) -> None:
        """Register callback, called when Sensor changes state."""
        self._update_callbacks.add(callback)

    def remove_update_cb(self, callback: Callable[[], None]) -> None:
        """Remove previously registered callback."""
        self._update_callbacks.discard(callback)

    def _publish_updates(self) -> None:
        """Schedule call all registered callbacks."""
        try:
            for callback in self._update_callbacks:
                callback()
        except Exception as err:
            _LOGGER.warning("Error while publishing sensor updates: %s", err)

    def register_new_sensor_cb(self, callback: Callable[[], None]) -> None:
        """Register callback, called when Sensor changes state."""
        self._new_sensor_callbacks.add(callback)

    def remove_new_sensor_cb(self, callback: Callable[[], None]) -> None:
        """Remove previously registered callback."""
        self._new_sensor_callbacks.discard(callback)

    def _publish_new_sensors(self) -> None:
        """Schedule call all registered callbacks."""
        try:
            for sensor in self._new_sensors:
                _LOGGER.debug("Publishing new sensor: %s", sensor)
                for callback in self._new_sensor_callbacks:
                    callback(sensor)
            self._new_sensors.clear()
        except Exception as err:
            _LOGGER.warning("Error while publishing new sensors: %s", err)
