import pytest

from aiospb.infra.encoding.protobuf.core import EncodingError, ProtobufPayloadEncoder
from aiospb.messages import Payload
from aiospb.shared import DataType, Metric, PropertySet, PropertyValue


class MetricBuilder:
    def __init__(self):
        self._ts = 111
        self._value = 1234
        self._datatype = DataType.Int16
        self._is_transient = False
        self._is_historical = False
        self._name = "Metric/Name"
        self._alias = 1
        self._properties = {}

    def with_timestamp(self, value):
        self._ts = value
        return self

    def with_value(self, value):
        self._value = value
        return self

    def with_alias(self, value):
        self._alias = value
        return self

    def with_datatype(self, value):
        self._datatype = value
        return self

    def with_property(self, name, value, datatype):
        self._properties[name] = PropertyValue(value, DataType[datatype])
        return self

    def is_historical(self):
        self._is_historical = True
        return self

    def is_transient(self):
        self._is_transient = True
        return self

    def with_name(self, value):
        self._name = value
        return self

    def build(self):
        properties = PropertySet(self._properties)
        return Metric(
            self._ts,
            self._value,
            self._datatype,
            self._alias,
            self._name,
            properties,
            self._is_transient,
            self._is_historical,
        )


class A_ProtobufPayloadEncoder:
    def should_encode_payload(self):
        metric = MetricBuilder().with_property("Quality", 192, "Int16").build()
        payload = Payload(timestamp=111, metrics=[metric], seq=1)

        encoding = ProtobufPayloadEncoder().encode(payload)
        decoded = ProtobufPayloadEncoder().decode(encoding)

        assert decoded == payload

    def should_encode_null_values(self):
        metric = (
            MetricBuilder()
            .with_value(None)
            .with_property("whatever", None, "String")
            .build()
        )
        payload = Payload(timestamp=111, metrics=[metric], seq=1)

        encoding = ProtobufPayloadEncoder().encode(payload)
        decoded = ProtobufPayloadEncoder().decode(encoding)

        assert decoded == payload

    def should_encode_negative_numbers(self):
        metric = MetricBuilder().with_value(-1).build()
        payload = Payload(timestamp=111, metrics=[metric], seq=1)

        encoding = ProtobufPayloadEncoder().encode(payload)
        decoded = ProtobufPayloadEncoder().decode(encoding)

        assert decoded == payload

    def should_encode_big_negative_numbers(self):
        metric = (
            MetricBuilder().with_value(-(2**33)).with_datatype(DataType.Int64).build()
        )
        payload = Payload(timestamp=111, metrics=[metric], seq=1)

        encoding = ProtobufPayloadEncoder().encode(payload)
        decoded = ProtobufPayloadEncoder().decode(encoding)

        assert decoded == payload

    def should_raise_encoding_error_if_client_error(self):
        metric = (
            MetricBuilder().with_value("error").with_datatype(DataType.Int64).build()
        )
        payload = Payload(timestamp=111, metrics=[metric], seq=1)

        with pytest.raises(EncodingError):
            ProtobufPayloadEncoder().encode(payload)
