from __future__ import annotations

from dataclasses import dataclass, field
from typing import List, Optional, Tuple


HEX_CHARS = set("0123456789abcdefABCDEF")


def _is_line_from_ecu(command: str) -> bool:
    return all(char in HEX_CHARS for char in command)


def _prepare_for_ecu(command: str) -> str:
    return "".join(char for char in command if char in HEX_CHARS)


@dataclass
class Request:
    command: Tuple[str, List[int]] | str
    bus_command_handler: Optional["BusCommandHandler"] = None
    received_anything: bool = False
    received_busy_please_wait: bool = False
    received_answers: int = 0
    expected_answers: int = field(default_factory=lambda: 2**31 - 1)
    timeout_override: Optional[int] = None

    @classmethod
    def from_string(cls, request: str) -> "Request":
        if _is_line_from_ecu(request):
            clean = _prepare_for_ecu(request)
            if len(clean) % 2 == 1:
                last = clean[-1]
                if not last.isdigit():
                    raise ValueError("Invalid expected response modifier")
                expected = int(last)
                clean = clean[:-1]
            else:
                expected = None
            bytes_ = _hex_to_bytes(clean)
            obj = cls(command=("bus", bytes_))
            from .bus_command_handler import BusCommandHandler
            obj.bus_command_handler = BusCommandHandler()
            if expected is not None:
                obj.override_expected_answers(expected)
            return obj
        return cls(command=request)

    @property
    def is_bus_command(self) -> bool:
        return isinstance(self.command, tuple) and self.command[0] == "bus"

    @property
    def is_adapter_command(self) -> bool:
        return isinstance(self.command, str)

    @property
    def bus_bytes(self) -> List[int]:
        if not self.is_bus_command:
            raise RuntimeError("Not a bus command")
        return self.command[1]

    @property
    def is_completed(self) -> bool:
        if not self.is_bus_command:
            return True
        if self.expected_answers <= 0:
            return True
        return self.received_answers >= self.expected_answers

    def did_receive_message(self, bytes_: List[int]) -> None:
        self.received_anything = True
        self.received_busy_please_wait = False
        if len(bytes_) == 3 and bytes_[0] == 0x7F and bytes_[2] == 0x78:
            self.received_busy_please_wait = True
            return
        if len(bytes_) == 4 and bytes_[0] == 0x03 and bytes_[1] == 0x7F and bytes_[3] == 0x78:
            self.received_busy_please_wait = True
            return
        self.received_answers += 1

    def did_receive_frame(self, bytes_: List[int]) -> None:
        if bytes_ and bytes_[0] == 0x30:
            return
        self.received_anything = True
        self.received_busy_please_wait = False
        if len(bytes_) >= 4 and bytes_[0] == 0x03 and bytes_[1] == 0x7F and bytes_[3] == 0x78:
            self.received_busy_please_wait = True
            return
        self.received_answers += 1

    def override_expected_answers(self, count: Optional[int]) -> None:
        if count is not None and count > 0:
            self.expected_answers = count
        else:
            self.expected_answers = 2**31 - 1

    def override_timeout(self, milliseconds: Optional[int]) -> None:
        if milliseconds is not None and milliseconds > 0:
            self.timeout_override = milliseconds
        else:
            self.timeout_override = None

    def effective_timeout(self, default: int, busy_timeout: int, is_busy: bool) -> int:
        if self.timeout_override is not None:
            return self.timeout_override
        return busy_timeout if is_busy else default


def _hex_to_bytes(value: str) -> List[int]:
    if not value:
        return []
    if len(value) % 2 == 1:
        value = "0" + value
    return [int(value[i:i + 2], 16) for i in range(0, len(value), 2)]
