import json

from marshmallow import (
    ValidationError,
    fields,
    post_load,
    pre_dump,
    validate,
    validates,
    validates_schema,
)

from maps_adv.adv_store.api.proto import creative_pb2
from maps_adv.common.protomallow import ProtobufSchema


class ImageSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Image

    type = fields.String()
    group_id = fields.String()
    image_name = fields.String()
    alias_template = fields.String(required=False)
    metadata = fields.String(required=False)

    @post_load
    def metadata_load(self, data):
        try:
            if "metadata" in data and data["metadata"] is not None:
                data["metadata"] = json.loads(data["metadata"])
        except json.JSONDecodeError:
            raise ValidationError("Invalid metadata field")

    @pre_dump
    def metadata_dump(self, data):
        if "metadata" in data and data is not None:
            data["metadata"] = json.dumps(data["metadata"])


class BillboardSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Billboard

    images = fields.Nested(ImageSchema, many=True)
    images_v2 = fields.Nested(ImageSchema, many=True)
    title = fields.String(required=False)
    description = fields.String(required=False)

    @validates_schema
    def validate_title_description(self, data):
        if (data.get("title") or data.get("description")) and not data.get(
            "images_v2", []
        ):
            raise ValidationError(
                "No image specified for new billboard format, but title and/or description is present"
            )

    @validates("title")
    def validate_title(self, title):
        if title is not None and len(title) > 19:
            raise ValidationError("Billboard title is too long (max 19)")

    @validates("description")
    def validate_description(self, description):
        if description is not None and len(description) > 19:
            raise ValidationError("Billboard description is too long (max 19)")


class PinSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Pin

    images = fields.Nested(ImageSchema, many=True)
    title = fields.String()
    subtitle = fields.String()


class LogoAndTextSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.LogoAndText

    images = fields.Nested(ImageSchema, many=True)
    text = fields.String()


class BannerSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Banner

    images = fields.Nested(ImageSchema, many=True)
    disclaimer = fields.String()
    show_ads_label = fields.Bool()
    title = fields.String()
    description = fields.String()
    terms = fields.String(missing="", default="", required=False)


class TextSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Text

    text = fields.String()
    disclaimer = fields.String()


class IconSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Icon

    images = fields.Nested(ImageSchema, many=True)
    title = fields.String()
    position = fields.Integer()


class PinSearchSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.PinSearch

    images = fields.Nested(ImageSchema, many=True)
    title = fields.String(missing="")
    organizations = fields.List(fields.Integer(), validate=validate.Length(min=1))


class ViaPointSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.ViaPoint

    images = fields.Nested(ImageSchema, many=True)
    button_text_active = fields.String(missing="")
    button_text_inactive = fields.String(missing="")
    description = fields.String(missing="")


class AudioBannerSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.AudioBanner

    images = fields.Nested(ImageSchema, many=True)
    left_anchor = fields.Decimal(as_string=True)
    audio_file_url = fields.String()


class CreativeSchema(ProtobufSchema):
    class Meta:
        pb_message_class = creative_pb2.Creative

    billboard = fields.Nested(BillboardSchema, required=False)
    pin = fields.Nested(PinSchema, required=False)
    logo_and_text = fields.Nested(LogoAndTextSchema, required=False)
    banner = fields.Nested(BannerSchema, required=False)
    text = fields.Nested(TextSchema, required=False)
    icon = fields.Nested(IconSchema, required=False)
    pin_search = fields.Nested(PinSearchSchema, required=False)
    via_point = fields.Nested(ViaPointSchema, required=False)
    audio_banner = fields.Nested(AudioBannerSchema, required=False)

    @post_load
    def add_type(self, data):
        creative_type, creative_data = list(data.items())[0]
        return dict(type_=creative_type, **creative_data)

    @pre_dump
    def detype(self, data):
        return {data.pop("type_"): data}
