# pylint: disable=missing-docstring
"""
Test the "external" interface in version 1.0.

This file should remain in the repository as long as we're still backwrds
compatible with 1.0. This includes call signatures *including* return types!
"""


import datetime
from collections import OrderedDict
from unittest.mock import patch

from x690.types import ObjectIdentifier, OctetString

import puresnmp as snmp
from puresnmp.const import Version
from puresnmp.pdu import VarBind
from puresnmp.util import BulkResult

from .. import ByteTester, readbytes, readbytes_multiple

OID = ObjectIdentifier.from_string
TESTS_SHOULD_RUN = True  # TODO should use importlib-metadata for this


def assert_of_types(values, types):
    """
    Checks the types of a collection of values. Raises an AssertionError if
    something is of the wrong type.

    This assumes that *values* and *types* are collections of tuples. Example::

        >>> values = [(1, 2), (1, 'foo')]
        >>> expected_types = [(int, int), (int, str)]
        >>> assert_of_types(values, expected_types)
    """
    for result, expected in zip(values, types):
        for result_value, result_type in zip(result, expected):
            if not isinstance(result_value, result_type):
                raise AssertionError(
                    "%r is not of type %r" % (result_value, result_type)
                )


class TestGet(ByteTester):
    def setUp(self):
        patcher = patch("puresnmp.api.raw.Transport")
        Transport = patcher.start()  # pylint: disable=invalid-name
        self.transport = Transport()
        self.transport.get_request_id.return_value = 1
        self.addCleanup(lambda: patcher.stop)

    def test_get(self):
        response = readbytes("apiv1/get_response.hex")
        self.transport.send.return_value = response
        result = snmp.get("192.168.1.1", "private", "1.3.6.1.2.1.1.2.0")
        self.assertEqual(result, "1.3.6.1.4.1.8072.3.2.10")
        self.assertTrue(self.transport.send.called, "method was not called")
        self.assertIsInstance(result, str)

    def test_multiget(self):
        response = readbytes("apiv1/multiget_response.hex")
        self.transport.send.return_value = response
        oids = ["1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.6.0"]
        result = snmp.multiget(
            "192.168.1.1", "private", oids, port=161, timeout=1
        )
        self.assertEqual(result, [b"root", b"On the move"])
        self.assertTrue(self.transport.send.called, "method was not called")
        for value in result:
            self.assertIsInstance(value, bytes)

    def test_multiget_v1(self):
        response = readbytes("apiv1/multiget_response.hex")
        self.transport.send.return_value = response
        oids = ["1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.6.0"]
        result = snmp.multiget(
            "192.168.1.1",
            "private",
            oids,
            port=161,
            timeout=1,
            version=Version.V1,
        )
        self.assertEqual(result, [b"root", b"On the move"])
        self.assertTrue(self.transport.send.called, "method was not called")
        for value in result:
            self.assertIsInstance(value, bytes)

    def test_getnext(self):
        response = readbytes("apiv1/getnext_response.hex")
        self.transport.send.return_value = response
        result = snmp.getnext(
            "192.168.1.1", "private", "1.3.6.1.2.1.1.6.0", port=161, timeout=1
        )
        expected = VarBind(OID("1.3.6.1.2.1.1.7.0"), 72)
        self.assertEqual(result, expected)
        self.assertTrue(self.transport.send.called, "method was not called")
        self.assertIsInstance(result.oid, ObjectIdentifier)
        self.assertIsInstance(result.value, int)

    def test_bulkget(self):
        response = readbytes("apiv1/bulkget_response.hex")
        self.transport.send.return_value = response
        result = snmp.bulkget(
            "192.168.1.1",
            "private",
            scalar_oids=["1.3.6.1.2.1.1.1.0"],
            repeating_oids=[
                "1.3.6.1.2.1.3.1.1",
                "1.3.6.1.2.1.4",
            ],
            max_list_size=10,
            port=161,
            timeout=1,
        )

        expected = BulkResult(
            {"1.3.6.1.2.1.1.2.0": "1.3.6.1.4.1.8072.3.2.10"},
            OrderedDict(
                [
                    ("1.3.6.1.2.1.3.1.1.1.12.1.172.17.0.1", 12),
                    ("1.3.6.1.2.1.4.1.0", 1),
                    ("1.3.6.1.2.1.3.1.1.2.12.1.172.17.0.1", b"\x02B@j\xbf\xcd"),
                    ("1.3.6.1.2.1.4.2.0", 64),
                    (
                        "1.3.6.1.2.1.3.1.1.3.12.1.172.17.0.1",
                        b"\xac\x11\x00\x01",
                    ),
                    ("1.3.6.1.2.1.4.3.0", 589),
                    ("1.3.6.1.2.1.4.4.0", 0),
                    ("1.3.6.1.2.1.4.5.0", 0),
                    ("1.3.6.1.2.1.4.6.0", 0),
                    ("1.3.6.1.2.1.4.7.0", 0),
                    ("1.3.6.1.2.1.4.8.0", 0),
                    ("1.3.6.1.2.1.4.9.0", 410),
                    ("1.3.6.1.2.1.4.10.0", 409),
                ]
            ),
        )
        self.assertEqual(result, expected)
        self.assertTrue(self.transport.send.called, "method was not called")
        key_types = {type(k) for k in result.listing.keys()}

        self.assertIsInstance(result.scalars["1.3.6.1.2.1.1.2.0"], str)
        self.assertEqual(key_types, {str})

        expected_types = [
            int,
            int,
            bytes,
            int,
            bytes,
            int,
            int,
            int,
            int,
            int,
            int,
            int,
            int,
        ]
        for value, type_ in zip(result.listing.values(), expected_types):
            self.assertIsInstance(value, type_)

    def test_bulkget_v1(self):
        response = readbytes("apiv1/bulkget_response.hex")
        self.transport.send.return_value = response
        result = snmp.bulkget(
            "192.168.1.1",
            "private",
            scalar_oids=["1.3.6.1.2.1.1.1.0"],
            repeating_oids=[
                "1.3.6.1.2.1.3.1.1",
                "1.3.6.1.2.1.4",
            ],
            max_list_size=10,
            port=161,
            timeout=1,
            version=Version.V1,
        )

        expected = BulkResult(
            {"1.3.6.1.2.1.1.2.0": "1.3.6.1.4.1.8072.3.2.10"},
            OrderedDict(
                [
                    ("1.3.6.1.2.1.3.1.1.1.12.1.172.17.0.1", 12),
                    ("1.3.6.1.2.1.4.1.0", 1),
                    ("1.3.6.1.2.1.3.1.1.2.12.1.172.17.0.1", b"\x02B@j\xbf\xcd"),
                    ("1.3.6.1.2.1.4.2.0", 64),
                    (