from marshmallow import (
    fields,
    pre_dump,
    validate,
)

from maps_adv.common.protomallow import (
    PbEnumField,
    ProtobufSchema
)
from maps_adv.geosmb.telegraphist.proto.v3 import (
    common_pb2,
    notifications_pb2,
)
from maps_adv.geosmb.telegraphist.server.lib.enums import Transport

PROTO_TO_ENUM_MAP = {
    "transports": [
        (common_pb2.TransportType.SMS, Transport.SMS),
        (common_pb2.TransportType.EMAIL, Transport.EMAIL),
        (common_pb2.TransportType.PUSH, Transport.PUSH),
        (common_pb2.TransportType.TELEGRAM, Transport.TELEGRAM)
    ]
}


class TransportSchema(ProtobufSchema):
    class Meta:
        pb_message_class = common_pb2.Transport

    type = PbEnumField(
        enum=Transport,
        pb_enum=common_pb2.TransportType,
        values_map=PROTO_TO_ENUM_MAP["transports"],
        required=True,
    )
    phones = fields.List(fields.Integer(validate=[validate.Range(min=1)]))
    emails = fields.List(fields.String(validate=[validate.Length(min=1)]))
    telegram_uids = fields.List(fields.Integer(validate=[validate.Range(min=1)]))


class OrgInfoSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.OrgInfo

    cabinet_link = fields.String(required=True, validate=[validate.Length(min=1)])
    company_link = fields.String(required=True, validate=[validate.Length(min=1)])


class RequestCreatedForBusinessSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.RequestCreated

    biz_id = fields.Integer(required=True, validate=[validate.Range(min=1)])
    org = fields.Nested(OrgInfoSchema, required=True)
    details_link = fields.String(required=True)


class CartOrderItemSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.CartOrderItem

    name = fields.String(required=True, validate=[validate.Length(min=1)])
    description = fields.String()
    price = fields.String()
    min_price = fields.String()
    image = fields.String()
    count = fields.Integer(required=True, validate=[validate.Range(min=1)])


class ContactsSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.Contacts

    name = fields.String(required=True, validate=[validate.Length(min=1)])
    phone = fields.String(required=True, validate=[validate.Length(min=1)])
    email = fields.String()


class OrderInfoSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.OrderInfo

    id = fields.Integer(required=True)
    cart_items = fields.Nested(
        CartOrderItemSchema, required=True, many=True, validate=[validate.Length(min=1)]
    )
    final_price = fields.String(required=True, validate=[validate.Length(min=1)])
    final_count = fields.Integer(required=True, validate=[validate.Range(min=1)])
    created_at = fields.String(required=True, validate=[validate.Length(min=1)])
    comment = fields.String()
    contacts = fields.Nested(ContactsSchema, required=True)


class LandingSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.LandingInfo

    domain = fields.String()
    url = fields.String()


class OrderCreatedSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.OrderCreatedV3

    biz_id = fields.Integer(required=True, validate=[validate.Range(min=1)])
    org = fields.Nested(OrgInfoSchema, required=True)
    details_link = fields.String(required=True)
    order = fields.Nested(OrderInfoSchema, required=True)
    landing = fields.Nested(LandingSchema, required=True)


class NotificationSchema(ProtobufSchema):
    class Meta:
        pb_message_class = notifications_pb2.Notification

    transport = fields.Nested(TransportSchema)
    request_created = fields.Nested(RequestCreatedForBusinessSchema)
    cart_order_created = fields.Nested(OrderCreatedSchema)


class NotificationTransportResultSchema(ProtobufSchema):
    class Meta:
        pb_message_class = common_pb2.NotificationTransportResult

    transport = PbEnumField(
        enum=Transport,
        pb_enum=common_pb2.TransportType,
        values_map=PROTO_TO_ENUM_MAP["transports"],
        required=True,
    )
    device_id = fields.String()
    passport_uid = fields.Integer()
    phone = fields.Integer()
    telegram_uid = fields.Integer()
    email = fields.String()
    error = fields.String()


class NotificationResultSchema(ProtobufSchema):
    class Meta:
        pb_message_class = common_pb2.NotificationResult

    results = fields.Nested(NotificationTransportResultSchema, many=True)

    @pre_dump
    def _to_flat(self, data: dict) -> dict:
        return {
            "results": [
                {"transport": transport, **result}
                for d in data
                for transport, result in d.items()
            ]
        }
