"""
    Это unittest для тестирования вашего класса Client из задания.

    Для запуска теста на локальном компьютере разместите код unittest-та
    и код решения в одном каталоге. Запустите тест при помощи команды:

        python -m unittest test_week5.py

    Обратите внимание на то, что ваш модуль должен называться client.py.
    Это не обязательное требование, если вы назвали мобуль по-другому, то
    просто измените его импорт в строке 25 на:
        from you_module_name import Client, ClientError

    Модуль должен содержать классы Client и ClientError.

    Этот unittest поможет вам выполнить задание.
    Успехов!
"""

import unittest
from unittest.mock import patch
from collections import deque

# импорт модуля с решением
from client import Client, ClientError


class ServerSocketException(Exception):
    pass


class ServerSocket:
    """Mock socket module"""

    def __init__(self):
        self.response_buf = deque()
        self.rsp_map = {
            b'put test 0.5 1\n': b'ok\n\n',
            b'put test 2.0 2\n': b'ok\n\n',
            b'put test 0.4 2\n': b'ok\n\n',
            b'put load 301 3\n': b'ok\n\n',
            b'get key_not_exists\n': b'ok\n\n',
            b'get test\n': b'ok\n'
                           b'test 0.5 1\n'
                           b'test 0.4 2\n\n',
            b'get get_client_error\n': b'error\nwrong command\n\n',
            b'get *\n': b'ok\n'
                        b'test 0.5 1\n'
                        b'test 0.4 2\n'
                        b'load 301 3\n\n',
        }

    def sendall(self, data):
        return self.send(data)

    def send(self, data):
        if data in self.rsp_map:
            self.response_buf.append(self.rsp_map[data])
        else:
            raise ServerSocketException(f"запрос не соответствует протоколу: {data}")

    def recv(self, bytes_count):
        try:
            rsp = self.response_buf.popleft()
        except IndexError:
            raise ServerSocketException("нет данных в сокете для чтения ответа")

        return rsp

    @classmethod
    def create_connection(cls, *args, **kwargs):
        return cls()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def __getattr__(self, feature):
        """ignore socket.connect, soket.bind, etc..."""
        pass


class TestClient(unittest.TestCase):
    @classmethod
    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def setUpClass(cls):
        cls.client = Client("127.0.0.1", 10000, timeout=2)

    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def test_client_put(self):
        metrics_for_put = [
            ("test", 0.5, 1),
            ("test", 2.0, 2),
            ("test", 0.4, 2),
            ("load", 301, 3),
        ]
        for metric, value, timestamp in metrics_for_put:
            try:
                self.client.put(metric, value, timestamp)
            except ServerSocketException as exp:
                message = exp.args[0]
                self.fail(f"Ошибка вызова client.put("
                          f"'{metric}', {value}, timestamp={timestamp})\n{message}")

    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def test_client_get_key(self):
        try:
            rsp = self.client.get("test")
        except ServerSocketException as exp:
            message = exp.args[0]
            self.fail(f"Ошибка вызова client.get('test')\n{message}")

        metrics_fixture = {
            "test": [(1, .5), (2, .4)],
        }
        self.assertEqual(rsp, metrics_fixture)

    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def test_client_get_all(self):
        try:
            rsp = self.client.get("*")
        except ServerSocketException as exp:
            message = exp.args[0]
            self.fail(f"Ошибка вызова client.get('*')\n{message}")

        metrics_fixture = {
            "test": [(1, .5), (2, .4)],
            "load": [(3, 301.0)]
        }
        self.assertEqual(rsp, metrics_fixture)

    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def test_client_get_not_exists(self):
        try:
            rsp = self.client.get("key_not_exists")
        except ServerSocketException as exp:
            message = exp.args[0]
            self.fail(f"Ошибка вызова client.get('key_not_exists')\n{message}")

        self.assertEqual({}, rsp, "check rsp eq {}")

    @patch("socket.create_connection", ServerSocket.create_connection)
    @patch("socket.socket", ServerSocket.create_connection)
    def test_client_get_client_error(self):
        try:
            self.assertRaises(ClientError,
                              self.client.get, "get_client_error")
        except ServerSocketException as exp:
            message = exp.args[0]
            self.fail(f"Некорректно обработано сообщение сервера об ошибке: {message}")
