"""Insteon inbound message data structure defintion."""
from binascii import hexlify
import logging
from typing import Tuple

from . import MessageBase
from ...constants import AckNak, MessageId
from ...data_types.message_flags import MessageFlags
from .message_definition import MessageDefinition
from .message_definitions import FLD_EXT_SEND_ACK, INBOUND_MSG_DEF, MessageField

_LOGGER = logging.getLogger(__name__)


def trim_data(raw_data: bytearray):
    """Trim bad data from the front of a message byte stream."""
    data_bytes = bytes(raw_data)
    while data_bytes and data_bytes[0] != 0x02:
        data_bytes = data_bytes[1:]
    return data_bytes


class Inbound(MessageBase):
    """Insteon inbound message data definition."""

    def __init__(self, msg_def: MessageDefinition, raw_data: bytearray):
        """Init the Inbound message class."""
        self._fields = msg_def.fields
        self._len = len(msg_def)
        field_vals = self._slice_data(msg_def.slices, raw_data)
        super().__init__(msg_def, **field_vals)

    def __len__(self):
        """Emit the length of the message."""
        return self._len

    def _slice_data(self, slices, raw_data):
        curr_slice = 2
        field_vals = {}
        for field in self._fields:
            val = field.type(raw_data[slices[curr_slice]])
            field_vals[field.name] = val
            curr_slice += 1
        return field_vals

    def __str__(self):
        """Emit the message in hex."""
        return hexlify(bytes(self)).decode()


def create(raw_data: bytearray) -> Tuple[Inbound, bytearray]:
    """Create a message from a raw byte array."""

    def _remaining_data(msg, data_bytes):
        if msg is not None:
            return data_bytes[len(msg) :]
        return data_bytes

    def _create_message(msg_def, data_bytes):
        msg = Inbound(msg_def, data_bytes)
        return msg, _remaining_data(msg, data_bytes)

    def _standard_message(data_bytes):
        flag_byte = 5
        flags = MessageFlags(data_bytes[flag_byte])
        if flags.is_extended:
            msg_def = MessageDefinition(MessageId.SEND_EXTENDED, FLD_EXT_SEND_ACK)
            if len(data_bytes) < len(msg_def):
                _LOGGER.debug("Full extended message not received")
                _LOGGER.debug("Returning: %s", data_bytes.hex())
                return None, bytearray(data_bytes)
        else:
            msg_def = INBOUND_MSG_DEF.get(MessageId.SEND_STANDARD)
        msg, remaining_data = _create_message(msg_def, data_bytes)
        return msg, remaining_data

    _LOGGER.debug("IN CREATE: %s", raw_data.hex())
    data_bytes = trim_data(raw_data)
    if len(data_bytes) < 2:
        _LOGGER.debug("Message less than 2 bytes")
        _LOGGER.debug("Returning: %s", data_bytes.hex())
        return None, bytearray(data_bytes)
    try:
        msg_id = MessageId(data_bytes[1])
    except ValueError as err:
        _LOGGER.debug("Error: %s", err)
        truncate = 1 if data_bytes[1] == 0x02 else 2
        data_bytes = trim_data(bytearray(data_bytes[truncate:]))
        _LOGGER.debug("Returning: %s", data_bytes.hex())
        return None, bytearray(data_bytes)
    if (
        msg_id in [MessageId.GET_IM_CONFIGURATION, MessageId.GET_IM_INFO]
        and len(data_bytes) >= 3
        and data_bytes[2] == 0x15
    ):
        msg_def = MessageDefinition(msg_id, [MessageField("ack", 1, AckNak)])
    else:
        msg_def = INBOUND_MSG_DEF.get(msg_id)

    if msg_def is not None:
        if len(data_bytes) < len(msg_def):
            _LOGGER.debug("Full message not received")
            _LOGGER.debug("Returning: %s", data_bytes.hex())
            return None, bytearray(data_bytes)

        if msg_id == MessageId.SEND_STANDARD:
            msg, remaining_data = _standard_message(data_bytes)
        else:
            msg, remaining_data = _create_message(msg_def, data_bytes)
        _LOGGER.debug("Returning: %s", remaining_data.hex())
        return msg, bytearray(remaining_data)

    return None, trim_data(data_bytes[1:])
