import random
import string
import uuid
from dataclasses import dataclass
from typing import Any

import pydash
from adafri.v1 import version
from firebase_admin.firestore import firestore

from ......utils import (
    ArrayUtils,
    BaseClass,
    DictUtils,
    RequestFields,
    get_object_model_class,
    get_request_fields,
    init_class_kwargs,
)
from ......utils.response import ApiResponse, Error, ResponseStatus, StatusCode
from ....models.adgroups.ads.base import BaseAd, BaseAdFieldsProps
from ....models.text import AssetText
from ....models.videos import VideoAsset
from ...models.asset import Asset as AssetNative
from .ad_fields import (
    RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS,
    RESPONSIVE_DISPLAY_ADS_COLLECTION,
    ResponsiveDisplayAdFields,
    ResponsiveDisplayAdFieldsProps,
)


def get_usage(key: str):
    if key == 'marketingImages':
        return ["image"], "rect"
    if key == 'squareMarketingImages':
        return ["image"], "sqr"
    if key=='squareLogoImages':
        return ["logo"], "sqr"
    if key in ['landscapeLogoImages', 'logoImages']:
        return ["logo"], "rect"
    return None

def filter_responsive_display_ad_request_fields(fields, default_fields = RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS):
    request_fields = get_request_fields(fields, default_fields, [default_fields[0]])
    if request_fields is None or bool(request_fields) is False:
        request_fields = [default_fields[0]]
    return request_fields

@dataclass(init=False)
class NativeAdsToPublish(BaseClass):
    id: str
    titles: list[AssetText]
    descriptions: list[AssetText]
    longHeadline: str
    brand: AssetText
    videosAssets: list[VideoAsset]
    marketingImages: list[AssetNative]
    squareMarketingImages: list[AssetNative]
    logoImages: list[AssetNative]
    landscapeLogoImages: list[AssetNative]
    is_removed: bool

    def normalize(self, images: list[AssetNative], key: str):
        if bool(images) is False:
            return []
        u = get_usage(key)
        if u is None:
            return []
        # print('normalize', u)
        # print('images', len(images))
        usage, useFor = u
        for im in images:
            if bool(im.usage) is False:
                im.usage = usage
            if bool(im.useFor) is False:
                im.useFor = useFor
            if bool(im.fullSizeInfo) and bool(im.fullSizeInfo.imageUrl):
                if bool(im.imageUrl) is False:
                    im.imageUrl = im.fullSizeInfo.imageUrl
                    im.file_url = im.fullSizeInfo.imageUrl
                if bool(im.width) is False:
                    im.width = im.fullSizeInfo.imageWidth
                if bool(im.height) is False:
                    im.height = im.fullSizeInfo.imageHeight
            if bool(im.imageFileSize) and bool(im.size) is False:
                im.size = im.imageFileSize
        return images
    def __init__(self, asset=None, **kwargs):
        super().__init__(**kwargs)
        (cls_object, keys, data_args) = init_class_kwargs(self, asset, None, None, None, [], **kwargs)
        for key in keys:
            if key=='titles':
                setattr(self, key, AssetText().fromListDict(cls_object[key]))
            elif key=='descriptions':
                setattr(self, key, AssetText().fromListDict(cls_object[key]))
            elif key=='brand':
                setattr(self, key, AssetText().from_dict(cls_object[key]))
            elif key=='videosAssets':
                setattr(self, key, VideoAsset().fromListDict(cls_object[key]))
            elif key in ['marketingImages', 'squareMarketingImages', 'logoImages', 'landscapeLogoImages']:
                images = AssetNative().fromListDict(cls_object[key])
                setattr(self, key,self.normalize(images, key))
            else:
                setattr(self, key, cls_object[key]) 
    def to_dict(self, field=None):
        return DictUtils.remove_none_values({
            "id": self.id,
            "titles": AssetText().toListDict(self.titles, None),
            "descriptions": AssetText().toListDict(self.descriptions, None),
            "longHeadline": self.longHeadline,
            "brand": self.brand.to_dict(None),
            "videosAssets": VideoAsset().toListDict(self.videosAssets, None),
            "marketingImages": AssetNative().toListDict(self.marketingImages, None),
            "squareMarketingImages": AssetNative().toListDict(self.squareMarketingImages, None),
            "logoImages": AssetNative().toListDict(self.logoImages, None),
            "landscapeLogoImages": AssetNative().toListDict(self.landscapeLogoImages, None),
            "is_removed": self.is_removed
        })
    @staticmethod
    def from_dict(obj: Any) -> 'NativeAdsToPublish':
        cls_object, keys = get_object_model_class(obj, NativeAdsToPublish, None);
        return NativeAdsToPublish(cls_object)
    @staticmethod
    def convert_to_responsive_display_ad(value):
        ad = ResponsiveDisplayAd();
        if bool(value.titles) is True:
            ad.headlines = value.titles.copy();
        if bool(value.descriptions) is True:
            ad.descriptions = value.descriptions;
        if bool(value.longHeadline) is True:
            ad.longHeadline = value.longHeadline;
        if bool(value.brand) is True:
            ad.businessName = value.brand;
        if bool(value.videosAssets) is True:
            ad.youtubeVideos = value.videosAssets;
        if bool(value.marketingImages) is True:
            ad.marketingImages = value.marketingImages.copy();
        if bool(value.squareMarketingImages) is True:
            ad.squareMarketingImages = value.squareMarketingImages.copy();
        if bool(value.logoImages) is True:
            ad.squareLogoImages = value.logoImages.copy();
        if bool(value.landscapeLogoImages) is True:
            ad.logoImages = value.landscapeLogoImages.copy();
        if getattr(value, 'owner', None) is not None:
            ad.owner = value.owner
        ad.is_published = False
        ad.adData = NativeAdsToPublish.from_dict(value.to_dict());
        return ad

    def to_responsive_display_ad(self, owner=None):
        if bool(owner) is True:
            self.owner = owner
        v = NativeAdsToPublish.convert_to_responsive_display_ad(self);
        return v
@dataclass(init=False)
class ResponsiveDisplayAd(BaseAd):
    headlines: list[AssetText]
    descriptions: list[AssetText]
    longHeadline: AssetText
    businessName: str
    marketingImages: list[AssetNative]
    squareMarketingImages: list[AssetNative]
    logoImages: list[AssetNative]
    squareLogoImages: list[AssetNative]
    youtubeVideos: list[VideoAsset]
    resourceName: str
    adData: NativeAdsToPublish
    displayUrl: str
    finalAppUrls: list[str]
    finalMobileUrls: list[str]
    finalUrls: list[str]
    responsive_display_ad: dict
    is_published: bool = False
    adgroup: Any | None = None
    __baseFields = ResponsiveDisplayAdFieldsProps
    class_object = None;

    def normalize(self, images: list[AssetNative], key: str):
        if bool(images) is False:
            return []
        u = get_usage(key)
        if u is None:
            return []
        # print('normalize', u)
        # print('images', len(images))
        usage, useFor = u
        # print('usage', usage, useFor)
        for im in images:
            # print('im.usage', im.usage)
            if bool(im.usage) is False:
                im.usage = usage
            # if bool(im.useFor) is False:
            im.useFor = useFor
            if bool(im.fullSizeInfo) and bool(im.fullSizeInfo.imageUrl):
                if bool(im.imageUrl) is False:
                    im.imageUrl = im.fullSizeInfo.imageUrl
                if bool(im.file_url) is False:
                    im.file_url = im.fullSizeInfo.imageUrl
                if bool(im.url) is False:
                    # print('im.url', im.url)
                    im.url = im.fullSizeInfo.imageUrl
                if bool(im.width) is False:
                    im.width = im.fullSizeInfo.imageWidth
                if bool(im.height) is False:
                    im.height = im.fullSizeInfo.imageHeight
            if bool(im.imageFileSize) and bool(im.size) is False:
                im.size = im.imageFileSize
        return images
    def __init__(self, ad=None, **kwargs):
        _ad = ad
        if isinstance(ad, str):
            _ad = {"id": ad}
        (cls_object, keys, data_args) = init_class_kwargs(self, _ad, RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS, ResponsiveDisplayAdFieldsProps, RESPONSIVE_DISPLAY_ADS_COLLECTION, ['id'], **kwargs)
        super().__init__(**{**data_args, "ad": _ad});
        for key in keys:
            if BaseAdFieldsProps.get(key) is None:
                if key == "adData":
                    setattr(self, key, NativeAdsToPublish().from_dict(cls_object[key]))
                elif key == 'headlines':
                    setattr(self, key, AssetText().fromListDict(cls_object[key]))
                elif key == 'descriptions':
                    setattr(self, key, AssetText().fromListDict(cls_object[key]))
                elif key in ['marketingImages', 'squareMarketingImages', 'logoImages', 'squareLogoImages']:
                    setattr(self, key, self.normalize(AssetNative().fromListDict(cls_object[key]), key))
                elif key == "longHeadline":
                    
                    if isinstance(cls_object[key], str):
                        setattr(self, key, AssetText.from_dict({'assetText': cls_object[key]}))
                    elif isinstance(cls_object[key], dict) is True and 'assetText' in cls_object[key]:
                        setattr(self, key, AssetText.from_dict(cls_object[key]))
                    else:
                        setattr(self, key, AssetText.from_dict({'assetText': None}))
                elif key == "businessName":
                    if isinstance(cls_object[key], str):
                        # setattr(self, key, AssetText.from_dict({'assetText': cls_object[key]}))
                        setattr(self, key, cls_object[key])
                    elif isinstance(cls_object[key], dict) is True and 'assetText' in cls_object[key]:
                        setattr(self, key, cls_object[key]['assetText'])
                        # setattr(self, key, AssetText.from_dict(cls_object[key]))
                    else:
                        setattr(self, key, None)
                elif key == 'youtubeVideos':
                    setattr(self, key, VideoAsset().fromListDict(cls_object[key]))
                else:
                    setattr(self, key, cls_object[key])
        if self.ad_id is None or self.ad_id == 0:
            self.is_published = False
        else:
            self.is_published = True
        self.class_object = ResponsiveDisplayAd


    @staticmethod
    def generate_model(_key_="default_value"):
        model = {};
        props = ResponsiveDisplayAdFieldsProps
        for k in DictUtils.get_keys(props):
            if k == "api_version":
                model[k] = version
                continue;
            if _key_ in props[k] and props[k][_key_] is not None:
                model[k] = props[k][_key_];
        
        # Generate random campaign name
        random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
        model['name'] = f'Ad Display {random_suffix}'
        return model;

    @staticmethod
    def from_dict(ad: Any, db=None, collection_name=None) -> 'ResponsiveDisplayAd':
        if isinstance(ad, dict) is True:
            if 'squareMarketingImage' in ad:
                ad['squareMarketingImages'] = ad['squareMarketingImage']
            if 'brand' in ad and 'businessName' not in ad:
                ad['businessName'] = ad['brand']
            if 'longHeadline' in ad and type(ad['longHeadline'])is str:
                ad['longHeadline'] = {"assetText": ad['longHeadline']} 
        cls_object, keys = get_object_model_class(ad, ResponsiveDisplayAd, ResponsiveDisplayAdFieldsProps);
        _ad = ResponsiveDisplayAd(cls_object, db=db, collection_name=collection_name)
        return _ad
    def generateAd(self, adgroup, data=None, only_generate = False):
        if adgroup is None:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error("Adgroup not found","INVALID_REQUEST", 1))

        model = ResponsiveDisplayAd.generate_model()
        model['accountId'] = adgroup.accountId
        model['clientCustomerId'] = adgroup.clientCustomerId
        if data is not None:
            model = DictUtils.merge_dict(data, model, True)
        # print('create ad with model', model)
        create = ResponsiveDisplayAd().create(model, adgroup, only_generate)
        if create.status == 'error':
            return create
        # print('fields', CAMPAIGN_DISPLAY_PICKABLE_FIELDS)
        values = create.data
        # values = DictUtils.omit_fields(values, RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS + ['owner', 'id_campagne', 'ad_group_id'])
        # print('values', values)
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, values, None)
    
    @staticmethod
    def property_keys():
        keys = [
                ResponsiveDisplayAdFields.marketingImages, 
                ResponsiveDisplayAdFields.squareMarketingImages,
                ResponsiveDisplayAdFields.logoImages,
                ResponsiveDisplayAdFields.squareLogoImages, 
                ResponsiveDisplayAdFields.name, 
                ResponsiveDisplayAdFields.adData,
                ResponsiveDisplayAdFields.headlines,
                ResponsiveDisplayAdFields.descriptions,
                ResponsiveDisplayAdFields.longHeadline,
                ResponsiveDisplayAdFields.businessName,
                ResponsiveDisplayAdFields.displayUrl,
                ResponsiveDisplayAdFields.finalUrls,
                ResponsiveDisplayAdFields.finalMobileUrls,
                ResponsiveDisplayAdFields.finalAppUrls]
        return keys
    @staticmethod
    def get_ad_group_props(value):
        if value is None:
            return {}
        return DictUtils.pick_fields(value, ResponsiveDisplayAd.property_keys())
    def to_dict(self, fields=None):
        base = super().to_dict(fields)
        adData = None;
        if self.adData is not None:
            adData = self.adData.to_dict()
        return DictUtils.remove_none_values({
            **base,
            "headlines": AssetText().toListDict(self.headlines, None),
            "descriptions": AssetText().toListDict(self.descriptions, None),
            "longHeadline": self.longHeadline.to_dict(),
            "businessName": self.businessName,
            "marketingImages": AssetNative().toListDict(self.marketingImages, None),
            "squareMarketingImages": AssetNative().toListDict(self.squareMarketingImages, None),
            "logoImages": AssetNative().toListDict(self.logoImages, None),
            "squareLogoImages": AssetNative().toListDict(self.squareLogoImages, None),
            "youtubeVideos": VideoAsset().toListDict(self.youtubeVideos, None),
            "adData": adData,
            "responsive_display_ad": self.responsive_display_ad
    })
    @staticmethod
    def findAd(id, aacid=None):
        print('findAd', id, aacid)
        isID = False
        try:
            id = int(id)
        except Exception as e:
            isID = True
        if isID is True:
            ad = ResponsiveDisplayAd.from_dict({"id": id}).get(RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS)
            if ad is None:
                return None
            return ad 
        if id is None or aacid is None or not isinstance(id, int) or not isinstance(aacid, str) or len(aacid) == 0:
            return None
        ad = ResponsiveDisplayAd(None).query(ResponsiveDisplayAd, "ad", query_params=[{"key": "accountId", "comp": "==", "value": aacid}, {"key": "ad_group_id", "comp": "==", "value": id}], first=True)
        return ad
    def getAds(self, adgroup, fields=['id', 'name'], response_type = 'dict'):
        isID = False
        if adgroup is None:
            return None
        adGroupId = adgroup.id
        # print('adgroup version', adgroup.api_version)
        if adgroup.api_version == 'v0' and getattr(adgroup, 'ad_group_id', None) is not None:
            adGroupId = adgroup.ad_group_id
        try:
            adGroupId = int(adGroupId)
        except:
            isID = True
        if isID is False and adgroup.accountId is None:
            return None
        # print('adGroupId', adGroupId)
        # print('isID', isID)
        ads = []
        queryParams = [{"key": "accountId", "comp": "==", "value": adgroup.accountId}]
        if isID is False:
            queryParams.append({"key": "ad_group_id", "comp": "==", "value": adGroupId})
        else :
            queryParams.append({"key": "adgroup_id_parent", "comp": "==", "value": adGroupId})
        ads_ = self.query(ResponsiveDisplayAd, "ad", query_params=queryParams)
        
        request_fields = filter_responsive_display_ad_request_fields(fields)
        # print('request_fields', request_fields)
        for ad in ads_:
            if response_type == 'class':
                ads.append(ad)
            else:
                if fields is None:
                    ads.append(ad.to_dict())
                    continue
                # ads.append(DictUtils.filter_by_fields(ad.to_dict(), request_fields))
                ads.append(ad.to_dict())
        return ads
    
    def get(self, fields=None):
        if bool(self.id) is None:
            return None;
        request_fields = filter_responsive_display_ad_request_fields(fields)
        doc = self.document_reference().get();
        if doc.exists is False:
            return None;
        doc_dict = doc.to_dict()
        if 'id' in doc_dict and bool(doc_dict['id']) is False:
            del doc_dict['id']
        data = {"id": doc.id, **doc_dict}
        return ResponsiveDisplayAd.from_dict(ad=ResponsiveDisplayAd.from_dict(ad=data).to_dict(),  db=self.db, collection_name=self.collection_name);

    def getByUserId(self,id, fields=None):
        ads = [];
        request_fields = filter_responsive_display_ad_request_fields(fields)
        docs = self.collection().where('owner','==',id).get();
        for doc in docs:
            data = doc.to_dict();
            data['id'] = doc.id;
            ads.append(ResponsiveDisplayAd.from_dict(ad=ResponsiveDisplayAd.from_dict(ad=data).to_json(request_fields),  db=self.db, collection_name=self.collection_name));
        return ads;
    def normalize_response(self):
        data = ResponsiveDisplayAd.from_dict(DictUtils.pick_fields(self.to_dict(), RESPONSIVE_DISPLAY_AD_PICKABLE_FIELDS))
        return ResponsiveDisplayAd.from_dict(DictUtils.omit_fields(data.to_dict(), ["owner", "createdBy"]))

    def getChangedFields(self, data):
        last_value = self.to_dict();
        filtered_value = pydash.pick(data, ResponsiveDisplayAdFields().filtered_keys('editable', True));
        new_value = DictUtils.merge_dict(filtered_value, last_value, True);
        _changed_fields = DictUtils.compare_nested_objects(last_value, new_value);
        changed_fields = ArrayUtils.filter(DictUtils.get_keys(_changed_fields), lambda x: str(x).find('.') == -1)
        return changed_fields

    def build_ad_image_asset(self, data: list[AssetNative], usage: list[str], useFor: str, test = False):
        images_assets = []
        error = None
        for image_asset in data:
            image_asset.accountId = self.accountId
            image_asset.owner = self.owner
            image_asset.usage = usage.copy()
            image_asset.useFor = useFor
            image_asset.adgroup = self.adgroup
            if not bool(image_asset.clientCustomerId):
                image_asset.clientCustomerId = self.clientCustomerId
            image: ApiResponse = image_asset.build_asset(test=test);
            if image.status == ResponseStatus.OK:
                images_assets.append(image.data)
            else:
                error = image
                break;
        if error is not None and bool(error) is True:
            return error
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, images_assets, None);

    def validate_asset_text(self, values: list[AssetText], title: str, minLength = 0, maxLength = 90):
        unique: list[AssetText] = []
        error = None
        for _value in values.copy():
            value = AssetText()
            value.assetText = _value.assetText.strip()
            validate = self.validate_token_string(value.assetText, f"{title}", minLength, maxLength, ["'"])
            if validate.status == ResponseStatus.ERROR:
                error = validate
                break
            text_value = self.sanitize_text(value.assetText, maxLength)
            if True in [u.assetText==text_value for u in unique]:
                error = ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"Duplicate {title} {value.assetText}", "INVALID_REQUEST", 1))
                break
            value.assetText = text_value
            unique.append(value)
        if error is not None:
            return error
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, unique, None);
    def validate_token_string(self, value: str | None, kind: str, min_length = 0, max_length = 25, chars_extended: list[str] | None = None):
        """
        Validate business name for Google Ads Responsive Display Ads
        
        Args:
            value (str): The business name to validate
            
        Returns:
            dict: Validation result with 'valid' boolean and 'message' string
        """
        MAX_LENGTH = max_length
        MIN_LENGTH = min_length
        
        # Check if business name is provided
        if not value:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} is required", "INVALID_REQUEST", 1))
        
        # Check if it's a string
        if not isinstance(value, str):
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} {value} must be a string", "INVALID_REQUEST", 1))
        
        # Strip whitespace
        value = value.strip()
        
        # Check if empty after stripping
        if not value:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} cannot be empty", "INVALID_REQUEST", 1))
        
        # Check length
        if len(value) < MIN_LENGTH:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} {value} must be at least {MIN_LENGTH} characters long", "INVALID_REQUEST", 1))
        if len(value) > MAX_LENGTH:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} {value} must be {MAX_LENGTH} characters or fewer. Current length: {len(value)}", "INVALID_REQUEST", 1))
        
        # Check for invalid characters (optional - based on your requirements)
        # Google Ads typically allows letters, numbers, spaces, and common punctuation
        chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -.,&()'
        if chars_extended is not None:
            chars += ''.join(chars_extended)
        invalid_chars = set(value) - set(chars)
        if invalid_chars:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"{kind} {value} contains invalid characters: {', '.join(invalid_chars)}", "INVALID_REQUEST", 1))
        
        # return {
        #     'valid': True,
        #     'message': 'Business name is valid',
        #     'length': len(value),
        #     'truncated_name': value[:MAX_LENGTH] if len(value) > MAX_LENGTH else value
        # }
        
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, None, None);

    def sanitize_text(self, value: str, max_length: int = 25) -> str:
        """
        Sanitize and truncate string to meet Google Ads requirements
        
        Args:
            value (str): The string to sanitize
            max_length (int): Maximum allowed length (default: 25)
            
        Returns:
            str: Sanitized string
        """
        if not value:
            return ""
        
        # Convert to string and strip whitespace
        sanitized = str(value).strip()
        
        # Remove invalid characters (keep only alphanumeric, spaces, and common punctuation)
        import re
        sanitized = re.sub(r'[^a-zA-Z0-9\s\-.,&()]', '', sanitized)
        
        # Truncate to max length
        if len(sanitized) > max_length:
            sanitized = sanitized[:max_length].rstrip()
        
        return sanitized

    def check_point_ad(self, test=False):
        if bool(self.name) is False:
            self.name = f"new Ad {uuid.uuid1()}"
        if bool(self.businessName):
            value_checkpoint = self.validate_token_string(self.businessName, "Business name")
            if value_checkpoint.status==ResponseStatus.ERROR:
                return value_checkpoint
            self.businessName = self.sanitize_text(self.businessName)
        headlines_checkpoint = self.validate_asset_text(self.headlines, "headline", 3, 30)
        if headlines_checkpoint.status==ResponseStatus.ERROR:
            return headlines_checkpoint
        self.headlines = headlines_checkpoint.data
        descriptions_checkpoint = self.validate_asset_text(self.descriptions, "description", 3, 90)
        if descriptions_checkpoint.status==ResponseStatus.ERROR:
            return descriptions_checkpoint
        self.descriptions = descriptions_checkpoint.data
        longHeadline_checkpoint = self.validate_asset_text([self.longHeadline], "longHeadline", 3, 90)
        if longHeadline_checkpoint.status==ResponseStatus.ERROR:
            return longHeadline_checkpoint
        self.longHeadline = longHeadline_checkpoint.data[0]
        marketing_images_checkpoint = self.build_ad_image_asset(self.marketingImages, ['image'], 'rect', test)
        if marketing_images_checkpoint.status == ResponseStatus.ERROR:
            return marketing_images_checkpoint
        self.marketingImages = marketing_images_checkpoint.data
        print('marketingImages', len(self.marketingImages))
        square_marketing_images_checkpoint = self.build_ad_image_asset(self.squareMarketingImages, ['image'], 'sqr', test)
        if square_marketing_images_checkpoint.status == ResponseStatus.ERROR:
            return square_marketing_images_checkpoint
        self.squareMarketingImages = square_marketing_images_checkpoint.data
        print('squareMarketingImages', len(self.squareMarketingImages))
        if bool(self.squareLogoImages):
            square_logo_images_checkpoint = self.build_ad_image_asset(self.squareLogoImages, ['logo'], 'sqr', test)
            if square_logo_images_checkpoint.status == ResponseStatus.ERROR:
                return square_logo_images_checkpoint
            self.squareLogoImages = square_logo_images_checkpoint.data
            print("sqaureLogoImages", len(self.squareLogoImages))
        if bool(self.logoImages):
            logo_images_checkpoint = self.build_ad_image_asset(self.logoImages, ['logo'], 'rect', test)
            if logo_images_checkpoint.status == ResponseStatus.ERROR:
                return logo_images_checkpoint
            self.logoImages = logo_images_checkpoint.data
            print("logoImages", len(self.logoImages))
        # print('check done ==>', self.to_dict())
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, self, None)

    def create(self, data, adgroup, only_generate = False):
        # creation_date = created_at * 1000
        # return DateUtils.from_timestamp(creation_date).isoformat()
        # try:
        # print('campaign account', data)
        self.adgroup = adgroup
        data_create = DictUtils.dict_from_keys(data, ResponsiveDisplayAdFields().filtered_keys('mutable', True))
        if bool(data_create) is False:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error("Invalid request","INVALID_REQUEST", 1))
        data_create = DictUtils.merge_dict({
                "accountId": adgroup.accountId,
                "owner": adgroup.owner,
                "clientCustomerId": adgroup.clientCustomerId,
                "adgroup_id_parent": adgroup.id,
                "createdBy": f"users/{adgroup.owner}"
            }, data_create.copy(),True)
        # print('data_create', data_create)
        document_id = self.collection().document().id
        # exist = self.query(ResponsiveDisplayAd, "ad", [{"key": "name", "comp": "==", "value": data_create['name']}], True)
        # if exist is not None:
        #     return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(f"Adgroup name {data_create['name']} already exists","INVALID_REQUEST", 1))
        model = ResponsiveDisplayAd.generate_model()
        model['id'] = document_id
        data_create = DictUtils.merge_dict(data_create, model, True)
        ad = ResponsiveDisplayAd.from_dict(dict(data_create).copy())
        ad.adgroup = adgroup
        checkpoint = ad.check_point_ad(only_generate)
        if checkpoint.status == ResponseStatus.ERROR:
            return checkpoint
        return checkpoint
        create = {**ad.to_dict(), 'createdAt': firestore.SERVER_TIMESTAMP}
        print('creating responsive search ad in few')
        if only_generate is False or only_generate is None:
            doc_ref = self.collection().document(document_id)
            doc_ref.set(create, True)
        ad = ResponsiveDisplayAd.from_dict(create).normalize_response()
        if ad is None:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error("Failed to create ad","INVALID_REQUEST", 1))
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, ad.normalize_response(), None)  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the created campaign object.  # Return the
        # except Exception as e:
        #     print(e)
        #     return {"error": "Exception creating campaign"};

    def update(self, data):
        try:
            last_value = self.to_dict();
            print('data update', data)
            # print('last', last_value);
            filtered_value = pydash.pick(data, ResponsiveDisplayAdFields().filtered_keys('editable', True));
            # print('filtered', filtered_value);
            new_value = DictUtils.merge_dict(filtered_value, last_value, True);
            _changed_fields = DictUtils.compare_nested_objects(last_value, new_value);
            changed_fields = ArrayUtils.filter(DictUtils.get_keys(_changed_fields), lambda x: str(x).find('.') == -1)
            data_update = DictUtils.dict_from_keys(filtered_value, changed_fields);
            if bool(data_update) is False:
                return ApiResponse(ResponseStatus.OK, StatusCode.status_200, data_update, None);
            # try:
            #     self.document_reference().set(data_update, merge=True)
            # except Exception as e:
            #     return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(str(e),"INVALID_REQUEST", 1))
            # return DictUtils.dict_from_keys(self.get(), changed_fields);
            # fields = changed_fields
            print('data_update ad', data_update)
            return ApiResponse(ResponseStatus.OK, StatusCode.status_200, data_update, None);
        except Exception as e:
            print(e)
            return None;

    def to_json(self, _fields=None):
        return self.to_dict()
    def remove(self, only_mark_as_removed=True):
        if self.is_removed is True:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_500, None, Error(f"Adgroup {self.id} already removed","INVALID_REQUEST", 1));
        try:
            if self.id is None:
                return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(f"Cannot identify Campaign with id {self.id}","INVALID_REQUEST", 1));
            if only_mark_as_removed:
                self.document_reference().set({"is_removed": True}, merge=True)
            else:
                self.document_reference().delete();
            return ApiResponse(ResponseStatus.OK, StatusCode.status_200, {"message": f"Campaign {self.id} deleted"}, None);
        except:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_400, None, Error(f"An error occurated while removing authorization code with id {self.id}","INVALID_REQUEST", 1));

