# mypy: disable-error-code="attr-defined"

import logging
import socket

from .error import Q3RconLibLoginError
from .packet import Request, ResponseFragment

logger = logging.getLogger(__name__)


class Base:
    def __init__(self, **kwargs):
        self.logger = logger.getChild(self.__class__.__name__)
        defaultkwargs = {
            'host': 'localhost',
            'port': 28960,
            'password': '',
            'default_timeout': 0.05,
            'timeouts': {},
        }
        kwargs = defaultkwargs | kwargs
        for attr, val in kwargs.items():
            setattr(self, attr, val)

        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        Request.set_password(self.password)
        self._request = Request()

        try:
            self._host_ip = socket.gethostbyname(self.host)
        except socket.gaierror:
            raise Q3RconLibLoginError(f"unable to resolve host '{self.host}'") from None

    def send(self, cmd: str) -> str:
        """send a rcon command to the server

        Args:
            cmd (str): command string that takes the form "<cmd> <args>"

        Returns:
            str: the entire command response (all fragments) as one string
        """

        self._sock.sendto(
            self._request.to_bytes(cmd),
            (self._host_ip, self.port),
        )
        self.logger.debug(f'sending: {cmd}')

        timeout: float = self.timeouts.get(
            cmd.split(' ', maxsplit=1)[0], self.default_timeout
        )
        self._sock.settimeout(timeout)

        fragments: list[str] = []
        try:
            while resp := self._sock.recv(4096):
                try:
                    if not resp.startswith(ResponseFragment.HEADER):
                        self.logger.debug(
                            'received invalid response fragment, ignoring'
                        )
                        continue

                    resp_str = resp.removeprefix(ResponseFragment.HEADER).decode()
                    fragments.append(resp_str)
                except (ValueError, UnicodeDecodeError):
                    self.logger.debug('received invalid response fragment, ignoring')
        except TimeoutError:
            self.logger.debug('finished collecting response fragments')

        return ''.join(fragments)

    def close(self):
        self._sock.close()
