from dataclasses import dataclass, field
from typing import Any, Dict, NamedTuple

from ..address import EURID, Address, BaseAddress, BroadcastAddress
from ..eep.id import EEP


class EntityValue(NamedTuple):
    """A lightweight container for semantic entity values."""

    value: Any
    """The interpreted/scaled value."""

    unit: str | None = None
    """The unit of the value (e.g., '°C', '%', 'lx')."""


@dataclass
class EEPMessageType:
    """Representation of an EEP message type."""

    id: int
    """A unique identifier for the message type."""

    description: str
    """A human-readable description of the message type."""


@dataclass
class EEPMessageValue:
    """Raw and interpreted value for a single EEP data field."""

    raw: int
    """The raw integer value of the data field as extracted from the message."""

    value: Any
    """The interpreted value of the data field according to the EEP profile's data field definition"""

    unit: str | None = None
    """The unit of the interpreted value (e.g., '°C', '%', 'kWh'). Can be dynamic based on message context."""


@dataclass
class EEPMessage:
    """An EEP message represents a message according to a specific EEP profile in a a dictionary of values extracted from the message according to the EEP profile's data fields. It should not be generated manually but using a proper EEPHandler.

    For convenience, it can include the sender's address, the destination address, the eep of the message, and the RSSI (signal strength).
    """

    sender: EURID | BaseAddress | None
    """The sender's address. This is optional and can be None if the sender is unknown or not relevant."""

    destination: Address = BroadcastAddress()
    """The destination address. This will only be different from broadcast, when addressed sending is used (i.e. for VLD telegrams)."""

    eep: EEP | None = None
    """The eep of the message. This is optional and can be None if the eep is unknown or not relevant."""

    rssi: int | None = None
    """The RSSI (signal strength) of the message. This is optional and can be None if the RSSI is unknown or not relevant."""

    message_type: EEPMessageType | None = None
    """The type of the message."""

    values: Dict[str, EEPMessageValue] = field(default_factory=dict)
    """A dictionary of values extracted from the message according to the EEP profile's data fields.

    Keys are EEP field IDs (e.g., 'R1', 'POS'); values are corresponding raw/interpreted pairs.
    """

    entities: Dict[str, EntityValue] = field(default_factory=dict)
    """A dictionary of semantically interpreted values keyed by ObservableUID.

    Keys are semantic observable UIDs (e.g., 'temperature', 'position').
    Values are EntityValue tuples containing (value, unit).
    """

    def __repr__(self) -> str:
        msg = f"EEPMessage(sender={self.sender.to_string()}, eep={self.eep}, message_type={self.message_type if self.message_type else 'default'}"
        if self.destination is not None and not self.destination.is_broadcast():
            msg += f", destination={self.destination.to_string()}"

        msg += f", values={self.values}, entities={self.entities})"
        return msg
