import datetime
from functools import lru_cache
from math import ceil
from django.conf import settings
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q, Min, Max
from developer.models import Developer
from development.models import BuildingTypeTranslate, ChangeRate, Currency, Development, Feature, FeatureTranslate, Flat, ONHArea, ONHDistrict, ONHPostcode, ONHStation, ONHZone, PaymentOptionTranslate, PaymentPlanTranslate
from development.schema import CurrencyType
from development.utils import flat_bedroom_type
from geo_location.models import City, CityTranslate, Country, CountryTranslate, District, LondonArea, LondonZipState, LondonZone, PostCode, PublicFacility, Region
from .forms import ContactUsForm, ErrorLogForm, RequestCallBackForm, SubscribeNewsletterForm
from .graphene_tools import PageDetail
from .models import ContactUs, ErrorLog, Faq, FaqTranslate, FilterElement, FilterList, FooterMenuTranslate, Language, RequestCallBack, SettingTranslate, SiteMap, SiteSettings, SubscribeNewsletter
from graphene_django.forms.mutation import DjangoModelFormMutation
from django.utils.text import slugify
from django.core.cache import cache
from django.db.models import Count, Q, F, Value

CACHE_TIME = settings.CACHE_TIME # cache in second

class MetaTagsType(graphene.ObjectType):
    """SEO Meta Tags Schema"""
    title = graphene.String()
    description = graphene.String()
    keywords = graphene.String()
    og_title = graphene.String()
    og_description = graphene.String()
    og_image = graphene.String()
    og_type = graphene.String()
    twitter_card = graphene.String()
    twitter_title = graphene.String()
    twitter_description = graphene.String()
    twitter_image = graphene.String()
    canonical_url = graphene.String()


class FlatNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    
class KeyFeaturesNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    icon = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"key_feature-{self['feature__slug']}"
    
    def resolve_icon(self, info):
        return self.get('feature__icon')


class LanguageType(DjangoObjectType):
    class Meta:
        model = Language
        fields = ["id", "name", "code"]
  
        
class FooterMenuType(graphene.ObjectType):
    name = graphene.String()
    link = graphene.String()

    def resolve_link(self, info):
        return self['footer_menu__link']
    
        
class SettingType(DjangoObjectType):
    class Meta:
        model = SiteSettings
        fields = ["id", "site_name"]


class SettingTranslateType(DjangoObjectType):
    class Meta:
        model = SettingTranslate
        fields = ["id", "site_name", "phone_number", "email", "short_description", "description", 
                "address", "rules", "about_us", "about_us_2", "privacy_policy", "terms_of_use", "about_us_home", "home_seo_text"]


class AboutUsType(graphene.ObjectType):
    text_1 = graphene.String()
    text_2 = graphene.String()
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    
    resolve_text_1 = lambda self, info: self.get('about_us')
    resolve_text_2 = lambda self, info: self.get('about_us_2')
    
    resolve_meta_tags = lambda self, info: MetaTagsType(
        title="about us",
        description=self.get('about_us')[:300],
        keywords=self.get('keywords'),
        og_title="about us",
        og_description=self.get('about_us')[:300],
        og_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        og_type="website",
        twitter_card="summary_large_image",
        twitter_title="about us",
        twitter_description=self.get('about_us')[:300],
        twitter_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        canonical_url=f"https://www.entralon.com/{self.get('language__code')}/about",
    )
    resolve_micro_data = lambda self, info: {
        "@context": "https://schema.org",
        "@type": "AboutPage",
        "name": "about us",
        "description": self.get('about_us')[:300],
        "image": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        "url": f"https://www.entralon.com/{self.get('language__code')}/about",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": f"https://www.entralon.com/{self.get('language__code')}/about",
        },
        "publisher": {
            "@type": "Organization",
            "name": "Entralon",
            "logo": {
                "@type": "ImageObject",
                "url": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
                "width": 600,
                "height": 60,
            }
        },
        "author": {
            "@type": "Person",
            "name": "Entralon",
            "url": f"https://www.entralon.com/",
        },
        "datePublished": self.get('created_at').isoformat(),
        "dateModified": self.get('updated_at').isoformat(),
        "breadcrumb": {
            "@type": "BreadcrumbList",
            "itemListElement": [
                {
                    "@type": "ListItem",
                    "position": 1,
                    "name": "Entralon",
                    "item": f"https://www.entralon.com/",
                },
                {
                    "@type": "ListItem",
                    "position": 2,
                    "name": "about us",
                    "item": f"https://www.entralon.com/{self.get('language__code')}/about",
                }
            ]
        }
    }


class TermsOfUseType(graphene.ObjectType):
    terms_of_use = graphene.String()
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    
    resolve_meta_tags = lambda self, info: MetaTagsType(
        title="terms of use",
        description=self.get('terms_of_use')[:300],
        keywords=self.get('keywords'),
        og_title="terms of use",
        og_description=self.get('terms_of_use')[:300],
        og_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        og_type="website",
        twitter_card="summary_large_image",
        twitter_title="terms of use",
        twitter_description=self.get('terms_of_use')[:300],
        twitter_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        canonical_url=f"https://www.entralon.com/{self.get('language__code')}/terms",
    )
    resolve_micro_data = lambda self, info: {
        "@context": "https://schema.org",
        "@type": "WebPage",
        "name": "terms of use",
        "description": self.get('terms_of_use')[:300],
        "image": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
        "url": f"https://www.entralon.com/{self.get('language__code')}/terms",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": f"https://www.entralon.com/{self.get('language__code')}/terms",
        },
        "publisher": {
            "@type": "Organization",
            "name": "Entralon",
            "logo": {
                "@type": "ImageObject",
                "url": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
                "width": 600,
                "height": 60,
                },
            },
        "author": {
            "@type": "Person",
            "name": "Entralon",
            "url": f"https://www.entralon.com/",
        },
        "datePublished": self.get('created_at').isoformat(),
        "dateModified": self.get('updated_at').isoformat(),
        "breadcrumb": {
            "@type": "BreadcrumbList",
            "itemListElement": [
                {
                    "@type": "ListItem",
                    "position": 1,
                    "name": "Entralon",
                    "item": f"https://www.entralon.com/",
                },
                {
                    "@type": "ListItem",
                    "position": 2,
                    "name": "terms of use",
                    "item": f"https://www.entralon.com/{self.get('language__code')}/terms",
                }
            ]
        }
    }


class PrivacyPolicyType(graphene.ObjectType):
    privacy_policy = graphene.String()
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    
    def resolve_meta_tags(self, info):
        return MetaTagsType(
            title="privacy policy",
            description=self.get('privacy_policy')[:300],
            keywords=self.get('keywords'),
            og_title="privacy policy",
            og_description=self.get('privacy_policy')[:300],
            og_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
            og_type="website",
            twitter_card="summary_large_image",
            twitter_title="privacy policy",
            twitter_description=self.get('privacy_policy')[:300],
            twitter_image=f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
            canonical_url=f"https://www.entralon.com/{self.get('language__code')}/privacy",
        )
    def resolve_micro_data(self, info):
        return {
            "@context": "https://schema.org",
            "@type": "WebPage",
            "name": "privacy policy",
            "description": self.get('privacy_policy')[:300],
            "image": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
            "url": f"https://www.entralon.com/{self.get('language__code')}/privacy",
            "mainEntityOfPage": {
                "@type": "WebPage",
                "@id": f"https://www.entralon.com/{self.get('language__code')}/privacy",
                "publisher": {
                "@type": "Organization",
                "name": "Entralon",
                "logo": {
                    "@type": "ImageObject",
                    "url": f"https://file.entralon.com/{self.get('site_settings__main_logo')}",
                    "width": 600,
                    "height": 60,
                    },
                },
                "author": {
                "@type": "Person",
                "name": "Entralon",
                "url": f"https://www.entralon.com/",
                },
                "datePublished": self.get('created_at').isoformat(),
                "dateModified": self.get('updated_at').isoformat(),
                "breadcrumb": {
                "@type": "BreadCrumbList",
                "itemListElement": [
                    {
                    "@type": "ListItem",
                    "position": 1,
                    "name": "Entralon",
                    "item": f"https://www.entralon.com/",
                    },
                    {
                    "@type": "ListItem",
                    "position": 2,
                    "name": "privacy policy",
                    "item": f"https://www.entralon.com/{self.get('language__code')}/privacy",
                    }
                ]
                }
            }
        }


class FaqType(DjangoObjectType):
    question = graphene.JSONString()
    answer = graphene.JSONString()
    class Meta:
        model = Faq
        fields = ["id", "question", "answer"]
    
    def resolve_question(self, info):
        out = {}
        for tr in self.translates.all():
            out[f"{tr.language.code}"] = tr.question 
        return out
    
    def resolve_answer(self, info):
        out = {}
        for tr in self.translates.all():
            out[f"{tr.language.code}"] = tr.answer
        return out


class FaqTranslateType(DjangoObjectType):
    
    class Meta:
        model = FaqTranslate
        fields = ["id", "question", "answer"]
    
    
class FaqNode(graphene.ObjectType):
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    node = graphene.List(FaqTranslateType)
    
    
    def resolve_meta_tags(self, info):
        ss = SiteSettings.objects.all().first()
        
        return MetaTagsType(
            title="faq",
            description="faq",
            keywords="faq, entralon, homes",
            og_title="faq",
            og_description="faq",
            og_image=f"https://file.entralon.com/{ss.main_logo}",
            og_type="website",
            twitter_card="summary_large_image",
            twitter_title="faq",
            twitter_description="faq",
            twitter_image=f"https://file.entralon.com/{ss.main_logo}",
            canonical_url=f"https://www.entralon.com/{self['language'].code}/faq",
        )
    
    def resolve_micro_data(self, info):
        ss = SiteSettings.objects.all().first()
        
        return {
            "@context": "https://schema.org",
            "@type": "WebPage",
            "name": "faq",
            "description": "faq",
            "image": f"https://file.entralon.com/{ss.main_logo}",
            "url": f"https://www.entralon.com/{self['language'].code}/faq",
            "mainEntityOfPage": {
                "@type": "WebPage",
                "@id": f"https://www.entralon.com/{self['language'].code}/faq",
                "publisher": {
                "@type": "Organization",
                "name": "Entralon",
                "logo": {
                    "@type": "ImageObject",
                    "url": f"https://file.entralon.com/{ss.main_logo}",
                    "width": 600,
                    "height": 60,
                    },
                },
                "author": {
                    "@type": "Person",
                    "name": "Entralon",
                    "url": f"https://www.entralon.com/",
                },
                "breadcrumb": {
                    "@type": "BreadCrumbList",
                    "itemListElement": [
                        {
                            "@type": "ListItem",
                            "position": 1,
                            "name": "Entralon",
                            "item": f"https://www.entralon.com/",
                        },
                        {
                            "@type": "ListItem",
                            "position": 2,
                            "name": "faq",
                            "item": f"https://www.entralon.com/{self['language'].code}/faq",
                        }
                    ]
                }
            }
        }
    
    
class CoordinatesType(graphene.ObjectType):
    lat = graphene.Float()
    lon = graphene.Float()


class AreaNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"district-{self.get('slug')}"
    
    def resolve_city(self, info):
        return f"city-{self.get('city__slug')}"
    
    
class AreaNodeFilterNode(graphene.ObjectType):
    node = graphene.List(AreaNode)
    page_info = graphene.Field(PageDetail)
    
    
class LondonAreaNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    def resolve_slug(self, info):
        return f"area-{self.get('slug')}"
    
    def resolve_city(self, info):
        return f"city-{self.get('city__slug')}"
    
    
class LondonZipCodeNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"zipcode-{self.get('slug')}"
        
    def resolve_city(self, info):
        return f"city-{self.get('city__slug')}"
    
    
class LondonZipCodeFilterNode(graphene.ObjectType):
    node = graphene.List(LondonZipCodeNode)
    page_info = graphene.Field(PageDetail)
    
    
class LondonZoneNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_city(self, info):
        return f"city-{self.get('city__slug')}"
    
    
class PriceNode(graphene.ObjectType):
    min = graphene.Float()
    max = graphene.Float()


class CompletionDateNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
   
        
class PostCodeNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_name(self, info):
        return self.out_code
    
    def resolve_slug(self, info):
        return self.out_code
  
    
class RegionNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    
class StationNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    district = graphene.String()
    icon = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"station-{self.get('slug')}"
    
    def resolve_icon(self, info):
        try: return self.get('icon')
        except: return None
    
    def resolve_city(self, info):
        try: return f"city-{self.get('city__slug')}"
        except: return None


class StationFilterNode(graphene.ObjectType):
    node = graphene.List(StationNode)
    page_info = graphene.Field(PageDetail)

    
class BedRoomNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
   
    
class CityNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"city-{self.get('city__slug')}"
    

class PaymentOptionNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"pay_opt-{self['payment_option__slug']}"
    
    
class PaymentPlanNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_name(self, info):
        return self['title']
    def resolve_slug(self, info):
        return f"pay_plan-{self['payment_plan__slug']}"
   
    
class BuildingTypeNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    is_selected = graphene.Boolean(default_value=False)
    
    def resolve_slug(self, info):
        return f"build_type-{self['building_type__slug']}"


class DeveloperNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    image = graphene.String()
    
    
    def resolve_slug(self, info):
        return f"developer-{self.slug}"
   
    
class DevelopmentNode(graphene.ObjectType):
    title = graphene.String()
    slug = graphene.String()
    image = graphene.String()
    
    def resolve_slug(self, info):
        return f"property-{self.slug}"


class SearchType(graphene.ObjectType):
    city = graphene.List(CityNode)
    developer = graphene.List(DeveloperNode)
    development = graphene.List(DevelopmentNode)
    district = graphene.List(AreaNode)
    station = graphene.List(StationNode)
    london_zip_code = graphene.List(LondonZipCodeNode)
    london_area = graphene.List(LondonAreaNode)
    london_zone = graphene.List(LondonZoneNode)
    

class FilterSearchType(graphene.ObjectType):
    node = graphene.Field(SearchType)
    page_info = graphene.Field(PageDetail)
    
    
@lru_cache(maxsize=100)
def _get_conversion_rate(from_currency, to_currency):
    # Cached currency conversion rate lookup
    rate = ChangeRate.objects.filter(
        from_currency__code=from_currency,
        to_currency__code=to_currency
    ).values_list('rate', flat=True).first()
    
    return rate
    
def _convert_prices(prices, target_currency, agg_func):
    # Helper method for currency conversion
    converted = []
    for price_data in prices:
        price = price_data['ck_price']
        source_currency = price_data['development__default_currency__code']
        
        if source_currency == target_currency:
            converted.append(price)
            continue
            
        rate = _get_conversion_rate(source_currency, target_currency)
        if rate:
            converted.append(price * rate)
            
    return agg_func(converted) if converted else None

class DevelopmentFilterType(graphene.ObjectType):
    city = graphene.List(CityNode)
    district = graphene.List(AreaNode)
    count = graphene.Int()
    min_price = graphene.Float()
    max_price = graphene.Float()
    bedrooms = graphene.List(FlatNode)
    completion_date = graphene.List(CompletionDateNode)
    payment_options = graphene.List(PaymentOptionNode)
    payment_plans = graphene.List(PaymentPlanNode)
    building_type = graphene.List(BuildingTypeNode)
    key_features = graphene.List(KeyFeaturesNode)
    post_code = graphene.List(PostCodeNode)
    region = graphene.List(RegionNode)
    station = graphene.List(StationNode)
    currency = graphene.List(CurrencyType)
    london_zip_code = graphene.List(LondonZipCodeNode)
    london_area = graphene.List(LondonAreaNode)
    london_zone = graphene.List(LondonZoneNode)

    
    def resolve_city(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_cities_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        language = self.get("language")
        cities = CityTranslate.objects.filter(
            language__code=language,
            city__developments__isnull=False,
            city__in=self.get('cities'),
        ).select_related('city', 'language').values('name', 'city__slug').distinct()
        cache.set(cache_key, cities, CACHE_TIME)
        return cities
    
    def resolve_london_zip_code(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_london_zip_code_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        london_zip_code = ONHPostcode.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(city__in=self.get('cities')).order_by("slug").distinct("slug")
        cache.set(cache_key, london_zip_code, CACHE_TIME)
        return london_zip_code
    
    def resolve_london_area(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_london_area_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        area = ONHArea.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(city__in=self.get('cities')).order_by("slug").distinct("slug")
        cache.set(cache_key, area, CACHE_TIME)
        return area

    def resolve_district(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_district_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        districts = ONHDistrict.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(city__in=self.get('cities')).order_by("slug").distinct("slug")
        cache.set(cache_key, districts, CACHE_TIME)
        return districts
    
    def resolve_london_zone(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_london_zone_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        zone = ONHZone.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(city__in=self.get('cities')).order_by("slug").distinct("slug")
        cache.set(cache_key, zone, CACHE_TIME)
        return zone
    
    def resolve_min_price(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_min_price_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached

        kwargs = self["kwargs"]
        default_currency_code = kwargs.get('currency', "GBP")
        cities = self.get('cities')
        if cities.count() == 1:
            country = cities[0].country
            if country.slug == "united-kingdom": default_currency_code = "GBP"
            elif country.slug == "united-arab-emirates": default_currency_code = "AED"
        
        min_prices = Flat.objects.filter(
            development__city__in=self.get('cities'),
            base_price__gt=0
        ).values('development__default_currency__code').annotate(
            ck_price=Min('base_price')
        )
        
        # Convert prices to target currency
        result = _convert_prices(min_prices, default_currency_code, min)
        cache.set(cache_key, result, CACHE_TIME)
        return result

    def resolve_max_price(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_max_price_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        
        kwargs = self["kwargs"]
        default_currency_code = kwargs.get('currency', "GBP")
        cities = self.get('cities')
        if cities.count() == 1:
            country = cities[0].country
            if country.slug == "united-kingdom": default_currency_code = "GBP"
            elif country.slug == "united-arab-emirates": default_currency_code = "AED"
        
        max_prices = Flat.objects.filter(
            development__city__in=self.get('cities'),
            base_price__gt=0
        ).values('development__default_currency__code').annotate(
            ck_price=Max('base_price')
        )
        
        # Convert prices to target currency
        result = _convert_prices(max_prices, default_currency_code, min)
        cache.set(cache_key, result, CACHE_TIME)
        return result
    
    def resolve_completion_date(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_completion_date_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        translates = {
            "en": "Ready To Move",
            "fa": "آماده سکونت",
            "ar": "جاهزة للسكن",
            "ru": "Готовая к переезду",
        }
        out = [CompletionDateNode(translates[self['language']], "completion-ready-to-move")]
        now = datetime.datetime.now()
        l = sorted(set(list(Development.objects.filter(completed_at__year__gte=now.year, city__in=self.get('cities')).values_list('completed_at__year', flat=True))))
        for l in l:
            out.append(CompletionDateNode(l,f"completion-{l}"))
        cache.set(cache_key, out, CACHE_TIME)
        return out
    
    def resolve_payment_options(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_payment_option_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        language = self.get("language")
        payment_options = PaymentOptionTranslate.objects.select_related('language', "payment_option").values("name", "payment_option__slug").filter(language__code=language, payment_option__developments__city__in=self.get('cities')).order_by('slug').distinct("slug")
        cache.set(cache_key, payment_options, CACHE_TIME)
        return payment_options
    
    def resolve_payment_plans(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_payment_plan_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        language = self.get("language")
        payment_plans = PaymentPlanTranslate.objects.select_related('language', "payment_plan").values("title", "payment_plan__slug").filter(language__code=language, payment_plan__development__city__in=self.get('cities')).order_by('slug').distinct("slug")
        cache.set(cache_key, payment_plans, CACHE_TIME)
        return payment_plans
    
    def resolve_building_type(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_building_type_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        language = self.get("language")
        building_types = BuildingTypeTranslate.objects.select_related('language', "building_type").values("name", "building_type__slug").filter(language__code=language, building_type__developments__city__in=self.get('cities')).order_by('slug').distinct("slug")
        cache.set(cache_key, building_types, CACHE_TIME)
        return building_types
    
    def resolve_key_features(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_key_features_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        language = self.get("language")
        features = FeatureTranslate.objects.select_related('language', "feature").values("name", "feature__slug", "feature__icon").filter(language__code=language, feature__development_features__development__city__in=self.get('cities')).exclude(slug__in=["floors", "property-type", "flats"]).distinct()
        cache.set(cache_key, features, CACHE_TIME)
        return features
    
    def resolve_bedrooms(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_bedrooms_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        out = []
        flats = Flat.objects.filter(development__city__in=self.get('cities'), bedrooms_num__lt=10000).distinct()
        l = sorted(set(list(flats.values_list('bedrooms_num', flat=True))))
        for l in l:
            name = flat_bedroom_type(l, self['language']) if l < 1 else l
            out.append({"name":name,"slug":f"bedroom-{l}"})
        cache.set(cache_key, out, CACHE_TIME)
        return out
    
    def resolve_station(self, info):
        city_key = self.get('city_key')
        cache_key = f"filter_station_{self['language']}"
        if city_key != "":
            cache_key += f"_{city_key}"
        cached = cache.get(cache_key)
        if cached:
            return cached
        station = ONHStation.objects.values('name', 'slug', 'icon', 'city__slug').filter(city__in=self.get('cities')).order_by("slug").distinct("slug")
        cache.set(cache_key, station, CACHE_TIME)
        return station
        
    def resolve_post_code(self, info):
        return PostCode.objects.filter(city__in=self.get('cities')).distinct()
    
    def resolve_region(self, info):
        return Region.objects.filter(city__in=self.get('cities')).distinct()
    
    def resolve_currency(self, info):
        return Currency.objects.filter(developments__city__in=self.get('cities')).distinct()
 
    
class RequestCallBackType(DjangoObjectType):
    class Meta:
        model = RequestCallBack
        fields = ["id", "first_name", "last_name", "phone_number", "email", "message", "language", "date",
                "utm_campaign", "utm_source", "utm_content", "utm_medium", "whatsapp_chat", "telegram_chat",
                'page_url', "development", "developer", "bedroom_num", "budget", "purpose", "payment_method", "time_to_move"]


class ContactUsType(DjangoObjectType):
    class Meta:
        model = ContactUs
        fields = ["id", "name", "phone_number", "email", "message"]


class SubscribeNewsletterType(DjangoObjectType):
    class Meta:
        model = SubscribeNewsletter
        fields = ["id", "email"]

        
class ErrorLogType(DjangoObjectType):
    class Meta:
        model = ErrorLog
        fields = ["id", "url", 'location', 'type', 'error']


class RequestCallBackMutation(DjangoModelFormMutation):
    call_back = graphene.Field(RequestCallBackType)

    class Meta:
        form_class = RequestCallBackForm
  
        
class ContactUsMutation(DjangoModelFormMutation):
    contact_us = graphene.Field(ContactUsType)

    class Meta:
        form_class = ContactUsForm
   
        
class SubscribeNewsletterMutation(DjangoModelFormMutation):
    subscribe_newsletter = graphene.Field(SubscribeNewsletterType)

    class Meta:
        form_class = SubscribeNewsletterForm


class ErrorLogMutation(DjangoModelFormMutation):
    error_log = graphene.Field(ErrorLogType)

    class Meta:
        form_class = ErrorLogForm


class FilterType(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()


class SelectedFilter(graphene.ObjectType):
    district = graphene.List(FilterType)
    bedroom_number = graphene.List(FilterType)
    london_zone = graphene.List(FilterType)
    london_area = graphene.List(FilterType)
    london_zip_code = graphene.List(FilterType)
    
    def resolve_district(self, info):
        return District.objects.filter(locations__developments__in=self['developments']).order_by("?", "name").distinct()[:15]
    def resolve_london_zip_code(self, info):
        return LondonZipState.objects.filter(locations__developments__in=self['developments']).order_by("?", "name").distinct()[:15]
    
    def resolve_london_area(self, info):
        return LondonArea.objects.filter(locations__developments__in=self['developments']).order_by("?", "name").distinct()[:15]
    
    def resolve_london_zone(self, info):
        return LondonZone.objects.filter(locations__developments__in=self['developments']).order_by("?", "name").distinct()[:15]
    
    def resolve_bedrooms(self, info):
        out = []
        flats = Flat.objects.filter(development__in=self['developments'], bedrooms_num__lt=10000).distinct()
        l = sorted(set(list(flats.values_list('bedrooms_num', flat=True))))
        for l in l:
            out.append(BedRoomNode(flat_bedroom_type(l, self['language']), f"bedroom-{l}"))
        return out[:15]

    
class breadcrumbNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    type = graphene.String()
    city = graphene.String()

    
class PageSeoType(graphene.ObjectType):
    meta_tags = graphene.Field(MetaTagsType)
    page_micro_data = graphene.JSONString()
    breadcrumb_micro_data = graphene.JSONString()
    organization_micro_data = graphene.JSONString()
    
    
class StatItem(graphene.ObjectType):
    id = graphene.Int()
    name = graphene.String()
    count = graphene.Int()
    link = graphene.String()

TRANSLATIONS = {
    "en": {
        "1_bedroom_houses": "1 Bedroom Houses",
        "2_bedroom_houses": "2 Bedroom Houses",
        "3_bedroom_houses": "3 Bedroom Houses",
        "4_bedroom_houses": "4 Bedroom Houses",
        "ready_to_move_houses": "Ready to Move Houses",
        "completion_2025": "Completed at 2025",
        "completion_2026": "Completed at 2026",
        "completion_2027": "Completed at 2027",
        "developers_in_city": "Developers",
        "price_up_to_500k": "Price Up to 500K {currency}",
        "price_up_to_1m": "Price Up to 1M {currency}",
        "price_up_to_1_5m": "Price Up to 1.5M {currency}",
        "price_up_to_2m": "Price Up to 2M {currency}",
        "in_city": "in {city}",
    },
    "fa": {
        "1_bedroom_houses": "خانه‌های یک خوابه",
        "2_bedroom_houses": "خانه‌های دو خوابه",
        "3_bedroom_houses": "خانه‌های سه خوابه",
        "4_bedroom_houses": "خانه‌های چهار خوابه",
        "ready_to_move_houses": "خانه‌های آماده تحویل ",
        "completion_2025": "خانه های تکمیل شده در 2025",
        "completion_2026": "خانه های تکمیل شده در 2026",
        "completion_2027": "خانه های تکمیل شده در 2027",
        "developers_in_city": "سازندگان",
        "price_up_to_500k": "خانه‌ها تا ۵۰۰ هزار {currency}",
        "price_up_to_1m": "خانه‌ها تا یک میلیون {currency}",
        "price_up_to_1_5m": "خانه‌ها تا یک میلیون و نیم {currency}",
        "price_up_to_2m": "خانه‌ها تا دو میلیون {currency}",
        "in_city": "در {city}",
    },
    "ar": {
        "1_bedroom_houses": "منازل بغرفة واحدة",
        "2_bedroom_houses": "منازل بغرفتي نوم",
        "3_bedroom_houses": "منازل بغرفة ثلاث نوم",
        "4_bedroom_houses": "منازل بغرفة اربع نوم",
        "ready_to_move_houses": "منازل جاهزة للتحريك",
        "completion_2025": "منازل مكتملة في 2025",
        "completion_2026": "منازل مكتملة في 2026",
        "completion_2027": "منازل مكتملة في 2027",
        "developers_in_city": "المطورون",
        "price_up_to_500k": "المنازل حتى 500 ألف {currency}",
        "price_up_to_1m": "المنازل حتى مليون {currency}",
        "price_up_to_1_5m": "المنازل حتى مليون ونصف {currency}",
        "price_up_to_2m": "المنازل حتى مليونين {currency}",
        "in_city": "في {city}",
    },
    "ru": {
        "1_bedroom_houses": "Дома с одной спальней",
        "2_bedroom_houses": "Дома с двумя спальнями",
        "3_bedroom_houses": "Дома с тремя спальнями",
        "4_bedroom_houses": "Дома с четырьмя спальнями",
        "ready_to_move_houses": "Готовые дома",
        "completion_2025": "Завершение в 2025",
        "completion_2026": "Завершение в 2026",
        "completion_2027": "Завершение в 2027",
        "developers_in_city": "Разработчики",
        "price_up_to_500k": "Дома до 500 тысяч {currency}",
        "price_up_to_1m": "Дома до миллиона {currency}",
        "price_up_to_1_5m": "Дома до миллиона и полутора миллионов {currency}",
        "price_up_to_2m": "Дома до двух миллионов {currency}",
        "in_city": "В {city}",
    }
}


class DevelopmentStatsNode(graphene.ObjectType):
    stats = graphene.List(StatItem)

    def resolve_stats(self, info):
        language = self.get('language')
        cities = self.get('cities')
        include_city = self.get('include_city')
        dynamic_stats = []
        n = 0
        for city in cities:
            country = city.country
            currency = country.default_currency
            if currency is None:
                if city.slug == "london":
                    currency = Currency.objects.get(code="GBP")
                else:
                    currency = Currency.objects.get(code="AED")
            f = Q(is_active=True, city=city, is_deleted=False, developer__is_active=True)
            try: city_name = city.translates.get(language=language).name
            except: city_name = city.name
            
            city_url = f"/{language.code}/city-{city.slug}"
            # stats = Development.objects.filter(is_active=True, city=city, is_deleted=False, developer__is_active=True).aggregate(
            #     one_bedroom_houses=Count('id', filter=Q(flats__bedrooms_num=1)),
            #     two_bedroom_houses=Count('id', filter=Q(flats__bedrooms_num=2)),
            #     three_bedroom_houses=Count('id', filter=Q(flats__bedrooms_num=3)),
            #     four_bedroom_houses=Count('id', filter=Q(flats__bedrooms_num=4)),
            #     ready_to_move_houses=Count('id', filter=Q(is_ready_to_move=True)),
            #     completion_2025=Count('id', filter=Q(completed_at__year="2025")),
            #     completion_2026=Count('id', filter=Q(completed_at__year="2026")),
            #     completion_2027=Count('id', filter=Q(completed_at__year="2027")),
            #     price_up_to_500k=Count('id', filter=Q(flats__base_price__range=(400000,500000))),
            #     price_up_to_1m=Count('id', filter=Q(flats__base_price__range=(400000, 1000000))),
            #     price_up_to_1_5m=Count('id', filter=Q(flats__base_price__range=(400000, 1500000))),
            #     price_up_to_2m=Count('id', filter=Q(flats__base_price__range=(400000, 2000000)))
            # )
            developments = Development.objects.filter(is_active=True, city=city, is_deleted=False, developer__is_active=True)
            one_bedroom_houses = developments.filter(flats__bedrooms_num=1).count()
            two_bedroom_houses = developments.filter(flats__bedrooms_num=2).count()
            three_bedroom_houses = developments.filter(flats__bedrooms_num=3).count()
            four_bedroom_houses = developments.filter(flats__bedrooms_num=4).count()
            ready_to_move_houses = developments.filter(is_ready_to_move=True).count()
            completion_2025 = developments.filter(completed_at__year="2025").count()
            completion_2026 = developments.filter(completed_at__year="2026").count()
            completion_2027 = developments.filter(completed_at__year="2027").count()
            price_up_to_500k = developments.filter(flats__base_price__range=(400_000,500_000)).distinct().count()
            price_up_to_1m = developments.filter(flats__base_price__range=(400_000, 1_000_000)).distinct().count()
            price_up_to_1_5m = developments.filter(flats__base_price__range=(400_000, 1_500_000)).distinct().count()
            price_up_to_2m = developments.filter(flats__base_price__range=(400_000, 2_000_000)).distinct().count()
            
            
            translations = TRANSLATIONS.get(language.code, TRANSLATIONS['en'])

            developers = Developer.objects.filter(city=city).count()
            if include_city: city_name = f" {translations['in_city'].format(city=city_name)}"
            else: city_name = ""
                    
            dynamic_stats +=[
                {
                    "id": n + 1,
                    "name": translations["1_bedroom_houses"] + city_name,
                    "count": one_bedroom_houses,
                    "link": f"{city_url}/bedroom-1"
                },
                {
                    "id": n + 2,
                    "name": translations["2_bedroom_houses"] + city_name,
                    "count": two_bedroom_houses,
                    "link": f"{city_url}/bedroom-2"
                },
                {
                    "id": n + 3,
                    "name": translations["3_bedroom_houses"] + city_name,
                    "count": three_bedroom_houses,
                    "link": f"{city_url}/bedroom-3"
                },
                {
                    "id": n + 4,
                    "name": translations["4_bedroom_houses"] + city_name,
                    "count": four_bedroom_houses,
                    "link": f"{city_url}/bedroom-4"
                },
                {
                    "id": n + 5,
                    "name": translations["ready_to_move_houses"] + city_name,
                    "count": ready_to_move_houses,
                    "link": f"{city_url}/completion-ready-to-move"
                },
                {
                    "id": n + 6,
                    "name": translations["completion_2025"] + city_name,
                    "count": completion_2025,
                    "link": f"{city_url}/completion-2025"
                },
                {
                    "id": n + 7,
                    "name": translations["completion_2026"] + city_name,
                    "count": completion_2026,
                    "link": f"{city_url}/completion-2026"
                },
                # {
                #     "id": n + 7,
                #     "name": translations["completion_2027"] + city_name,
                #     "count": completion_2027,
                #     "link": f"{city_url}/completion-2027"
                # },
                {
                    "id": n + 8,
                    "name": translations["price_up_to_500k"].format(currency=currency.symbol) + city_name,
                    "count": price_up_to_500k,
                    "link": f"{city_url}?priceMin=400000&priceMax=500000"
                },
                {
                    "id": n + 9,
                    "name": translations["price_up_to_1m"].format(currency=currency.symbol) + city_name,
                    "count": price_up_to_1m,
                    "link": f"{city_url}?priceMin=400000&priceMax=1000000"
                },
                {
                    "id": n + 10,
                    "name": translations["price_up_to_1_5m"].format(currency=currency.symbol) + city_name,
                    "count": price_up_to_1_5m,
                    "link": f"{city_url}?priceMin=400000&priceMax=1500000"
                },
                {
                    "id": n + 11,
                    "name": translations["price_up_to_2m"].format(currency=currency.symbol) + city_name,
                    "count": price_up_to_2m,
                    "link": f"{city_url}/?priceMin=400000&priceMax=2000000"
                },
                {
                    "id": n + 12,
                    "name": translations["developers_in_city"] + city_name,
                    "count": developers,
                    "link": f"/developers"
                },
            ]
            n = len(dynamic_stats)
        
        return dynamic_stats # [StatItem(name=item["name"], count=item["count"], link=item["link"]) for item in dynamic_stats]


class FilterItemNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    city = graphene.String()
    
class FilterListNode(graphene.ObjectType):
    station = graphene.List(FilterItemNode)
    district = graphene.List(FilterItemNode)
    postcode = graphene.List(FilterItemNode)
    bedroom = graphene.List(FilterItemNode)
    area = graphene.List(FilterItemNode)
    zone = graphene.List(FilterItemNode)
    key_feature = graphene.List(FilterItemNode)
    payment_option = graphene.List(FilterItemNode)
    completion_date = graphene.List(FilterItemNode)
    building_type = graphene.List(FilterItemNode)
    min_price = graphene.Int()
    max_price = graphene.Int()
    currency = graphene.String()
    
    def resolve_station(self, info):
        return self.station.all()
    
    def resolve_district(self, info):
        return self.district.all()
    
    def resolve_postcode(self, info):
        return self.postcode.all()
    
    def resolve_bedroom(self, info):
        return self.bedroom.all()
    
    def resolve_area(self, info):
        return self.area.all()
    
    def resolve_zone(self, info):
        return self.zone.all()
    
    def resolve_key_feature(self, info):
        return self.key_feature.all()
    
    def resolve_payment_option(self, info):
        return self.payment_option.all()
    
    def resolve_completion_date(self, info):
        return self.completion_date.all()
    
    def resolve_building_type(self, info):
        return self.building_type.all()
    
    def resolve_min_price(self, info):
        return self.min_price
    
    def resolve_max_price(self, info):
        return self.max_price
    
    def resolve_currency(self, info):
        country = self.city.country
        
        currency = country.default_currency
        if currency is None:
            code = "GBP" if country.slug == "united-kingdom" else "UAE"
            currency = Currency.objects.get(code=code)
        return currency.symbol
    

class SiteMapNode(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    slug = graphene.String()
    link = graphene.String()
    
    def resolve_name(self, info):
        return self['obj_name']
    
    def resolve_slug(self, info):
        return f"{self['pre_slug']}{self['slug']}"
    
    def resolve_link(self, info):
        return f"{self['pre_link']}{self['slug']}"
    
class SiteMapType(graphene.ObjectType):
    developer = graphene.List(SiteMapNode)
    development = graphene.List(SiteMapNode)
    city = graphene.List(SiteMapNode)
    station = graphene.List(SiteMapNode)
    district = graphene.List(SiteMapNode)
    zip_code = graphene.List(SiteMapNode)
    area = graphene.List(SiteMapNode)
    zone = graphene.List(SiteMapNode)
    key_feature = graphene.List(SiteMapNode)
    payment_option = graphene.List(SiteMapNode)
    
    
    def resolve_developer(self, info):
        language = self['language']
        pre_slug = 'developer-'
        pre_link = f'/{language.code}/developers/{pre_slug}'
        developer = Developer.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(is_active=True, is_deleted=False)
        return developer
    
    def resolve_development(self, info):
        language = self['language']
        pre_slug = 'property-'
        pre_link = f'/{language.code}/{pre_slug}'
        development = Development.objects.annotate(obj_name=F('title'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(is_active=True, is_deleted=False)
        return development
    
    def resolve_city(self, info):
        language = self['language']
        pre_slug = 'city-'
        cities = None
        pre_link = f'/{language.code}/{pre_slug}'
        cities = CityTranslate.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city__is_featured=True, language=language)
        return cities
    
    def resolve_station(self, info):
        language = self['language']
        pre_slug = 'station-'
        stations = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            station = ONHStation.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city=city)
            if stations: stations = stations.union(station)
            else: stations = station
        return stations
    
    def resolve_district(self, info):
        language = self['language']
        pre_slug = 'district-'
        districts = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            district = ONHDistrict.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city=city)
            if districts: districts = districts.union(district)
            else: districts = district
        return districts
    
    def resolve_zip_code(self, info):
        language = self['language']
        pre_slug = 'zipcode-'
        zip_codes = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            zip_code = ONHPostcode.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city=city)
            if zip_codes: zip_codes = zip_codes.union(zip_code)
            else: zip_codes = zip_code
        return zip_codes
    
    def resolve_area(self, info):
        language = self['language']
        pre_slug = 'area-'
        areas = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            area = ONHArea.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city=city)
            if areas: areas = areas.union(area)
            else: areas = area
        return areas
    
    def resolve_zone(self, info):
        language = self['language']
        pre_slug = ''
        zones = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            zone = ONHZone.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(city=city)
            if zones: zones = zones.union(zone)
            else: zones = zone
        return zones
    
    def resolve_key_feature(self, info):
        language = self['language']
        pre_slug = 'key_feature-'
        key_features = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            key_feature = FeatureTranslate.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(language=language)
            if key_features: key_features = key_features.union(key_feature)
            else: key_features = key_feature
        return key_features
    
    def resolve_payment_option(self, info):
        language = self['language']
        pre_slug = 'pay_opt-'
        payment_options = None
        for city in City.objects.filter(is_featured=True):
            pre_link = f'/{language.code}/city-{city.slug}/{pre_slug}'
            payment_option = PaymentOptionTranslate.objects.annotate(obj_name=F('name'), pre_link=Value(pre_link), pre_slug=Value(pre_slug)).values('id', 'obj_name', 'pre_link', 'pre_slug', 'slug').filter(language=language)
            if payment_options: payment_options = payment_options.union(payment_option)
            else: payment_options = payment_option
        return payment_options
    
    
class SiteMapModeType(graphene.ObjectType):
    id = graphene.ID()
    type = graphene.String()
    name = graphene.String()
    url = graphene.String()
    
    
    def resolve_type(self, info):
        return self.type.title.title()
    
class SiteMapPageNode(graphene.ObjectType):
    node = graphene.List(SiteMapModeType)
    page_info = graphene.Field(PageDetail)
    
        
class Query(graphene.ObjectType):
    general_data = graphene.Field(SettingTranslateType, language=graphene.String())
    search_data = graphene.Field(FilterSearchType,search=graphene.String(required=True), page=graphene.Int(), page_size=graphene.Int(), language=graphene.String())
    filters = graphene.Field(DevelopmentFilterType, city=graphene.String(), developer=graphene.List(graphene.String), 
                            ready_to_move=graphene.Boolean(), available=graphene.Boolean(), language=graphene.String(), currency=graphene.String(), district=graphene.List(graphene.String), 
                            london_zip_code=graphene.List(graphene.String), london_area=graphene.List(graphene.String), london_zone=graphene.List(graphene.String), station=graphene.List(graphene.String))
    

    filters_list = graphene.Field(FilterListNode, language=graphene.String(), city=graphene.String())

    
    site_map = graphene.Field(SiteMapType, language=graphene.String())
    # sitemap = graphene.List(SiteMapModeType, language=graphene.String(), page=graphene.Int(), page_size=graphene.Int())
    sitemap = graphene.Field(SiteMapPageNode, language=graphene.String(), page=graphene.Int(), page_size=graphene.Int())
    
    station = graphene.Field(StationFilterNode, city=graphene.String(), search=graphene.String(), development=graphene.List(graphene.String), limit=graphene.Boolean(), page=graphene.Int(), page_size=graphene.Int(), language=graphene.String())
    district = graphene.Field(AreaNodeFilterNode, city=graphene.String(), search=graphene.String(), limit=graphene.Boolean(), page=graphene.Int(), page_size=graphene.Int(), language=graphene.String())
    london_zip_code = graphene.Field(LondonZipCodeFilterNode, city=graphene.String(), search=graphene.String(), limit=graphene.Boolean(), page=graphene.Int(), page_size=graphene.Int(), language=graphene.String())
    
    faq = graphene.Field(FaqNode, language=graphene.String())
    selected_filters = graphene.Field(SelectedFilter)
    footer_menu = graphene.List(FooterMenuType, language=graphene.String())
    breadcrumb = graphene.List(breadcrumbNode, slug=graphene.List(graphene.String), language=graphene.String())
    page_seo = graphene.Field(PageSeoType, slug=graphene.List(graphene.String), language=graphene.String())
    about_us = graphene.Field(AboutUsType, language=graphene.String())
    terms_of_use = graphene.Field(TermsOfUseType, language=graphene.String())
    privacy_policy = graphene.Field(PrivacyPolicyType, language=graphene.String())
    home_seo_text = graphene.Field(graphene.String(), language=graphene.String())
    privacy_notice = graphene.Field(graphene.String(), language=graphene.String())
    about_us_home = graphene.Field(graphene.String(), language=graphene.String())
    development_stats = graphene.Field(DevelopmentStatsNode, city=graphene.String(), language=graphene.String())
    
    def resolve_site_map(root, info, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
            
        return {'language': lang}
    
    def resolve_sitemap(root, info, language="en", page=1, page_size=1000):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        start = (page - 1) * page_size
        end = page * page_size
        qs = SiteMap.objects.only('id', 'name', 'type', 'url').filter(language=lang)
        return {
            "node": qs[start:end],
            "page_info": {
                "has_next_page": end < qs.count(),
                "has_previous_page": page > 1,
                "current_page": page,
                "page_size": page_size,
                "count": qs.count(),
                "pages_count": ceil(qs.count() / page_size),
            }
            
        }
    

    def resolve_filters_list(root, info, city, language="en"):

        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        f = Q(language=lang)

        if city: 
            city = city.replace('city-', '', 1)
            f &= Q(city__slug=city)
        else: f &= Q(city=None)
        try: return FilterList.objects.select_related(
            'language', 'city', 'min_price', 'max_price').prefetch_related(
                'station').get(f)
        except Exception as e:
            return None
        
    def resolve_faq(root, info, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        return {
            "node":FaqTranslate.objects.filter(faq__is_active=True, language=lang),
            "language":lang
        }
    
    def resolve_selected_filters(root, info, language="en"):
        f = Q(is_active=True, is_deleted=False)
        developments = Development.objects.filter(f).distinct()
        return {"developments":developments, "language":language}
        
    def resolve_general_data(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.filter(language__code="en").first()
        return ts
        
    def resolve_search_data(root, info, search, page=1, page_size=5, language="en"):
        cache_key = f"search_data_{search}_{page}_{page_size}_{language}"
                
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 50:
            page_size = 50
        
        search = search.replace("+", " ")
        search_split = search.split(" ")
        search_list = []
        search_list.append(search)
        search_list.append(slugify(search, allow_unicode=True))
        for l in ["-", "_", ""]:
            w = l.join(search_split)
            if w not in search_split:
                search_list.append(w)
            
        filter_s = filter_t_s = None
        for q in search_list:
            if q is not None and len(q) > 1:
                if filter_s is None: filter_s = Q(similar_name__icontains=q)
                else: filter_s |= Q(similar_name__icontains=q)
                if filter_t_s is None: filter_t_s = Q(similar_name__icontains=q) | Q(translates__similar_name__icontains=q)
                else: filter_t_s |= Q(similar_name__icontains=q) | Q(translates__similar_name__icontains=q)
                
        cities = CityTranslate.objects.filter(
            city__in=City.objects.filter(filter_t_s, developments__isnull=False, developments__is_active=True), language__code=language).select_related(
            'city', 'language').values(
            'name', 'city__slug'
            ).distinct()
        
        # station = PublicFacility.objects.filter(filter_s, type__slug__in=["subway-stations","train-stations"], developments__isnull=False).order_by('slug').distinct('slug')
        station = ONHStation.objects.values('name', 'slug', 'icon', 'city__slug').filter(Q(name__icontains=search) | Q(slug__icontains=search), developments__isnull=False).order_by("slug").distinct("slug")
        # district = District.objects.filter(filter_s).order_by('slug').distinct('slug')
        district = ONHDistrict.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(Q(name__icontains=search) | Q(slug__icontains=search), developments__isnull=False).order_by("slug").distinct("slug")
        london_zone = ONHZone.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(Q(name__icontains=search) | Q(slug__icontains=search), developments__isnull=False).order_by('slug').distinct('slug')
        London_area = ONHArea.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(Q(name__icontains=search) | Q(slug__icontains=search), developments__isnull=False).order_by('slug').distinct('slug')
        # london_zip_code = LondonZipState.objects.filter(filter_s).order_by('slug').distinct('slug')
        london_zip_code = ONHPostcode.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        ).filter(Q(name__icontains=search) | Q(slug__icontains=search), developments__isnull=False).order_by("slug").distinct("slug")
        
        developers = Developer.objects.filter(Q(name__icontains=search) | Q(slug__icontains=search) | Q(translates__name__icontains=search) | Q(translates__slug__icontains=search), is_active=True, is_deleted=False).distinct()
        developments = Development.objects.filter(Q(title__icontains=search) | Q(slug__icontains=search) | Q(translates__title__icontains=search) | Q(translates__slug__icontains=search), is_active=True, is_deleted=False, developer__is_active=True).distinct()
        count = max(cities.count(), developers.count(), developments.count(), station.count(), district.count(), london_zone.count(), London_area.count(), london_zip_code.count())
        pages_count = ceil(count / page_size)
        if page > pages_count: page = pages_count
        if page < 1: page = 1
        start = 0 + (page - 1) * page_size
        end = start + page_size
        has_next_page = end < count
        
        result = {
            'node':{
                "city": cities[start:end],
                "developer": developers[start:end],
                "development": developments[start:end],
                "district": district[start:end],
                "station": station[start:end],
                "london_zone": london_zone[start:end],
                "london_area": London_area[start:end],
                "london_zip_code": london_zip_code[start:end],
            },
            "page_info": {
                "has_next_page": has_next_page,
                "has_previous_page": page > 1,
                "current_page": page,
                "page_size": page_size,
                "count": count,
                "pages_count": pages_count,
            }
        }
        
        cache.set(cache_key, result, CACHE_TIME)
        
        return result
    
    def resolve_filters(root, info, language="en", **kwargs):
               
        cities_filter = None
        if "city" in kwargs and kwargs.get('city'):
            city_slugs = kwargs['city'].replace("city-", "")
            kwargs['city'] = city_slugs
            if cities_filter: cities_filter &= Q(slug=city_slugs)
            else: cities_filter = Q(slug=city_slugs)
        elif "district" in kwargs and kwargs.get('district'):
            district_slugs = [item.replace("district-", "") for item in kwargs['district']]
            if cities_filter: cities_filter &= Q(onh_districts__slug__in=district_slugs)
            else: cities_filter = Q(onh_districts__slug__in=district_slugs)
        elif "london_zip_code" in kwargs and kwargs.get('london_zip_code'):
            zipcode_slugs = [item.replace("zipcode-", "") for item in kwargs['london_zip_code']]
            if cities_filter: cities_filter &= Q(onh_postcodes__slug__in=zipcode_slugs)
            else: cities_filter = Q(onh_postcodes__slug__in=zipcode_slugs)
        elif "london_zone" in kwargs and kwargs.get('london_zone'):
            if cities_filter: cities_filter &= Q(onh_zone__slug__in=kwargs['london_zone'])
            else: cities_filter = Q(onh_zone__slug__in=kwargs['london_zone'])
        elif "london_area" in kwargs and kwargs.get('london_area'):
            area_slugs = [item.replace("area-", "") for item in kwargs['london_area']]
            if cities_filter: cities_filter &= Q(onh_area__slug__in=area_slugs)
            else: cities_filter = Q(onh_area__slug__in=area_slugs)
        if cities_filter: cities = City.objects.filter(cities_filter).distinct()
        else: cities = City.objects.filter(is_featured=True)
        city_key = '_'.join(f"{d}" for d in cities.values_list('id', flat=True))
        result = {
            "cities": cities.only('id', 'name', 'slug'),
            "city_key": city_key,
            "language": language,
            "kwargs": kwargs
        }
        # cache.set(cache_key, result, CACHE_TIME)
        return result

    def resolve_station(root, info, limit=False, page=1, page_size=60, language="en", **kwargs):
        cache_key = f"station_query_{limit}_{page}_{page_size}_{language}"
        if "city" in kwargs:
            city_slug = kwargs['city'].replace("city-", "")
            cache_key += city_slug
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 100:
            page_size = 100
        f = None
        
        qs = ONHStation.objects.select_related('city').values(
            'name', 'slug', 'icon', 'city__slug'
        )
        
        filters = {}
        if "city" in kwargs:
            filters['city__slug'] = city_slug
        if "development" in kwargs:
            filters['developments__slug__in'] = kwargs['development']
        if "search" in kwargs:
            filters['name__icontains'] = kwargs['search']
        # qs = PublicFacility.objects.filter(f_pf).order_by("slug").distinct("slug")
        if filters:
            qs = qs.filter(**filters)
            
        qs = qs.order_by('slug').distinct('slug')
        count = qs.count()
        if limit:
            pages_count = ceil(count / page_size)
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0 + (page - 1) * page_size
            end = start + page_size
            has_next_page = end < count
        else:
            pages_count = 1
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0
            end = count
            has_next_page = False
            
        result = {
            "node": qs[start:end],
            "page_info": {
                "has_next_page": has_next_page,
                "has_previous_page": page > 1,
                "current_page": page,
                "page_size": page_size, 
                "count": count,
                "pages_count": pages_count
            }
        }
        
        cache.set(cache_key, result, CACHE_TIME)
    
        return result

    def resolve_district(root, info, limit=False, page=1, page_size=60, language="en", **kwargs):
        cache_key = f"district_query_{limit}_{page}_{page_size}_{language}"
        if "city" in kwargs:
            city_slug = kwargs['city'].replace("city-", "")
            cache_key += city_slug
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 100:
            page_size = 100
        
        filters = {}
        if "city" in kwargs:
            filters['city__slug'] = city_slug
        if "search" in kwargs:
            filters['name__icontains'] = kwargs['search']
        qs = ONHDistrict.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        )
        if filters:
            qs = qs.filter(**filters)
        qs = qs.order_by("slug").distinct("slug")
        count = qs.count()
        if limit:
            pages_count = ceil(count / page_size)
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0 + (page - 1) * page_size
            end = start + page_size
            has_next_page = end < count
        else:
            pages_count = 1
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0
            end = count
            has_next_page = False
        
        result = {
            "node": qs[start:end],
            "page_info": {
                "has_next_page": has_next_page,
                "has_previous_page": page > 1,
                "current_page": page,
                "page_size": page_size, 
                "count": count,
                "pages_count": pages_count
            }
        }
        
        cache.set(cache_key, result, CACHE_TIME)
    
        return result

    def resolve_london_zip_code(root, info, limit=False, page=1, page_size=60, language="en", **kwargs):
        cache_key = f"london_zip_code_query_{limit}_{page}_{page_size}_{language}"
        if "city" in kwargs:
            city_slug = kwargs['city'].replace("city-", "")
            cache_key += city_slug
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 100:
            page_size = 100
        filters = {}
        if "city" in kwargs:
            filters['city__slug'] = city_slug
        if "search" in kwargs:
            filters['name__icontains'] = kwargs['search']
        qs = ONHPostcode.objects.select_related('city').values(
            'name', 'slug', 'city__slug'
        )
        if filters:
            qs = qs.filter(**filters)
        qs = qs.order_by("slug").distinct("slug")
        count = qs.count()
        if limit:
            pages_count = ceil(count / page_size)
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0 + (page - 1) * page_size
            end = start + page_size
            has_next_page = end < count
        else:
            pages_count = 1
            if page > pages_count: page = pages_count
            if page < 1: page = 1
            start = 0
            end = count
            has_next_page = False
        
        result = {
            "node": qs[start:end],
            "page_info": {
                "has_next_page": has_next_page,
                "has_previous_page": page > 1,
                "current_page": page,
                "page_size": page_size, 
                "count": count,
                "pages_count": pages_count
            }
        }
        
        cache.set(cache_key, result, CACHE_TIME)
    
        return result
    
    def resolve_footer_menu(root, info, language="en"):
        cache_key = f"footer_menu_query_{language}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        
        out = FooterMenuTranslate.objects.values('name', 'footer_menu__link').filter(language=lang, footer_menu__is_active=True).order_by("footer_menu__priority")
        
        cache.set(cache_key, out, CACHE_TIME)
        return out
      
    def resolve_breadcrumb(root, info, slug, language="en"):
        cache_key = f"breadcrumb_query_{language}"
        cache_key += f"_{'-'.join(item for item in slug)}"
        
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        out = []
        base_url = f"https://entralon.com/{lang.code}"
        city = None
        for s in slug:
            if s.startswith("city-"):
                city = CityTranslate.objects.filter(city__slug=s.replace("city-", "",1), language=lang).first()
                if city is not None:
                    out.append({"name":city.name,"slug":s, "url":f"{base_url}/{s}", "type":"city", "order":2})
            elif s.startswith("area-"):
                area = LondonArea.objects.filter(slug=s.replace("area-", "",1)).first()
                if area is not None:
                    out.append({"name":area.name,"slug":s, "url":f"{base_url}/city-{area.city.slug}/{s}", "type":"area", "city":f"city-{area.city.slug}", "order":3})
            elif s.startswith("zone-"):
                zone = LondonZone.objects.filter(slug=s).first()
                if zone is not None:
                    out.append({"name":zone.name,"slug":s, "url":f"{base_url}/city-{zone.city.slug}/{s}", "type":"zone", "city":f"city-{zone.city.slug}", "order":4})
            elif s.startswith("zipcode-"):
                zipcode = ONHPostcode.objects.filter(slug=s.replace("zipcode-", "",1)).first()
                if zipcode is not None:
                    out.append({"name":zipcode.name,"slug":s, "url":f"{base_url}/city-{zipcode.city.slug}/{s}", "type":"zipcode", "city":f"city-{zipcode.city.slug}", "order":5})
            elif s.startswith("district-"):
                district = ONHDistrict.objects.filter(slug=s.replace("district-", "",1)).first()
                if district is not None:
                    out.append({"name":district.name,"slug":s, "url":f"{base_url}/city-{district.city.slug}/{s}", "type":"district", "city":f"city-{district.city.slug}", "order":5})
            elif s.startswith("station-"):
                station = ONHStation.objects.filter(slug=s.replace("station-", "",1)).first()
                if station is not None:
                    out.append({"name":station.name,"slug":s, "url":f"{base_url}/city-{station.city.slug}/{s}", "type":"station", "city":f"city-{station.city.slug}", "order":6})
            elif s.startswith("key_feature-"):
                key_feature = FeatureTranslate.objects.filter(feature__slug=s.replace("key_feature-","",1), language=lang).first()
                if key_feature is not None:
                    out.append({"name":key_feature.name,"slug":s, "url":f"{base_url}/city-{city.slug}/{s}", "type":"key_feature", "city":f"city-{city.slug}", "order":7})
            elif s.startswith("pay_opt-"):
                pay_option = PaymentOptionTranslate.objects.filter(payment_option__slug=s.replace("pay_opt-","",1), language=lang).first()
                if pay_option is not None:
                    out.append({"name":pay_option.name,"slug":s, "url":f"{base_url}/city-{city.slug}/{s}", "type":"pay_option", "city":f"city-{city.slug}", "order":8})
            elif s.startswith("bedroom-"):
                try:    
                    num = int(s.replace("bedroom-", "",1))
                    out.append({"name":flat_bedroom_type(num, lang=lang.code),"slug":s, "url":f"{base_url}/city-{city.slug}/{s}", "type":"bedroom", "city":f"city-{city.slug}", "order":9})
                except: pass
            elif s.startswith("completion-"):
                translates = {
                "en": "Ready To Move",
                "fa": "آماده سکونت",
                "ar": "جاهزة للسكن",
                "ru": "Готовая к переезду",
                }
                try:    
                    try: name = int(s.replace("completion-", "",1))
                    except: name = translates[lang.code]
                    out.append({"name":name,"slug":s, "url":f"{base_url}/city-{city.slug}/{s}", "type":"completion", "city":f"city-{city.slug}", "order":10})
                except: pass
            elif s.startswith("property-"):
                development = Development.objects.filter(slug=s.replace("property-", "",1)).first()
                if development is not None: out.append({"name":development.title, "slug":s, "url":f"{base_url}/{s}",  "type":"property", "city":f"city-{development.city.slug}", "order":10})
                    
            
            
        out = sorted(out, key=lambda d: d['order'])
        cache.set(cache_key, out, CACHE_TIME)
        return out
    
    def resolve_page_seo(root, info, slug, language="en"):
        cache_key = f"page_seo_query_{language}"
        cache_key += f"_{'-'.join(item for item in slug)}"
        
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        setting = SiteSettings.objects.first()
        try: setting_t = SettingTranslate.objects.get(site_settings=setting, language=lang)
        except: setting_t = SettingTranslate.objects.get(site_settings=setting, language__code="en")
        breadcrumb_array = []
        base_url = f"https://entralon.com/{lang.code}"
        breadcrumb_array.append({"@type": "ListItem", "name":f"Home", "item": f"{base_url}", "position":1})
        title = f"{setting_t.site_name} | {setting_t.short_description}"
        description = f"{setting_t.description}"    
        keywords = f"{setting_t.keywords}"
        image = "https://file.entralon.com/favicon.ico"
        canonical_url = base_url
        city = None
        for s in slug:
            if s.startswith("city-"):
                city = CityTranslate.objects.filter(city__slug=s.replace("city-", "",1), language=lang).first()
                if city is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":city.name, "item": f"{base_url}/{s}", "position":3})
            elif s.startswith("area-"):
                area = LondonArea.objects.filter(slug=s.replace("area-", "",1)).first()
                if area is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":area.name, "item": f"{base_url}/city-{area.city.slug}/{s}", "position":4})
            elif s.startswith("zone-"):
                zone = LondonZone.objects.filter(slug=s).first()
                if zone is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":zone.name, "item": f"{base_url}/city-{zone.city.slug}/{s}", "position":5})
            elif s.startswith("zipcode-"):
                zipcode = ONHPostcode.objects.filter(slug=s.replace("zipcode-", "",1)).first()
                if zipcode is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":zipcode.name, "item": f"{base_url}/city-{zipcode.city.slug}/{s}", "position":6})
            elif s.startswith("district-"):
                district = ONHDistrict.objects.filter(slug=s.replace("district-", "",1)).first()
                if district is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":district.name, "item": f"{base_url}/city-{district.city.slug}/{s}", "position":7})
            elif s.startswith("station-"):
                station = ONHStation.objects.filter(slug=s.replace("station-", "",1)).first()
                if station is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":station.name, "item": f"{base_url}/city-{station.city.slug}/{s}", "position":8})
            elif s.startswith("key_feature-"):
                key_feature = FeatureTranslate.objects.filter(feature__slug=s.replace("key_feature-","",1), language=lang).first()
                if key_feature is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":key_feature.name, "item": f"{base_url}/city-{city.slug}/{s}", "position":9})
            elif s.startswith("pay_opt-"):
                pay_option = PaymentOptionTranslate.objects.filter(payment_option__slug=s.replace("pay_opt-","",1), language=lang).first()
                if pay_option is not None:
                    breadcrumb_array.append({"@type": "ListItem", "name":pay_option.name, "item": f"{base_url}/city-{city.slug}/{s}", "position":10})
            elif s.startswith("bedroom-"):
                try:    
                    num = int(s.replace("bedroom-", "",1))
                    breadcrumb_array.append({"@type": "ListItem", "name":flat_bedroom_type(num, lang=lang.code), "item": f"{base_url}/city-{city.slug}/{s}", "position":11})
                except: pass
            elif s.startswith("completion-"):
                translates = {
                "en": "Ready To Move",
                "fa": "آماده سکونت",
                "ar": "جاهزة للسكن",
                "ru": "Готовая к переезду",
                }
                try:    
                    try: name = int(s.replace("completion-", "",1))
                    except: name = translates[lang.code]
                    breadcrumb_array.append({"@type": "ListItem", "name":f"{name}", "item": f"{base_url}/city-{city.slug}/{s}", "position":12})
                except: pass
            elif s.startswith("property-"):
                development = Development.objects.filter(slug=s.replace("property-", "",1)).first()
                if development is not None: breadcrumb_array.append({"@type": "ListItem", "name":development.title, "item": f"{base_url}/{s}", "position":12})
        
        
        breadcrumb_array = sorted(breadcrumb_array, key=lambda d: d['position'])
        breadcrumb_list = {
            "@context": "https://schema.org",
            "@type": "BreadcrumbList",
            "itemListElement": breadcrumb_array
        }
        
        page_micro = {
            "@context": "https://schema.org",
            "@type": "WebPage",
            "name": title,
            "url": base_url,
            "description": description,
            "isPartOf": {
                "@type": "WebSite",
                "url": "https://entralon.com"
            }
        }
        organization_micro_data = {
            "@context": "https://schema.org",
            "@type": "Organization",
            "name": f"{setting_t.site_name}",
            "url": "https://entralon.com/",
            "logo": "https://entralon.com/favicon.ico",
            "contactPoint": {
                "@type": "ContactPoint",
                "telephone": f"{setting_t.phone_number}",
                "contactType": "customer service"
            },
            "address": {
                "@type": "PostalAddress",
                "streetAddress": f"{setting_t.address}",
                "addressLocality": f"{setting_t.address_locality}",
                "addressCountry": f"{setting_t.address_country}",
            }
        }

        
        out = {
            "meta_tags": {
                "title": title,
                "description": description,
                "keywords": keywords,
                "og_title": title,
                "og_description": description,
                "og_image": image,
                "og_type": "website",
                "twitter_card": "summary",
                "twitter_title": title,
                "twitter_description": description,
                "twitter_image": image,
                "canonical_url": canonical_url
            },
            "page_micro_data": page_micro,
            "breadcrumb_micro_data": breadcrumb_list,
            "organization_micro_data": organization_micro_data,
            
        }
        cache.set(cache_key, out, CACHE_TIME)
        return out

    def resolve_about_us(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.values('about_us', 'about_us_2', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.values('about_us', 'about_us_2', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code="en").first()
        return ts
        
    def resolve_terms_of_use(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.values('terms_of_use', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.values('terms_of_use', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code="en").first()
        return ts
    
    def resolve_privacy_policy(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.values('privacy_policy', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.values('privacy_policy', "keywords", "created_at", "updated_at", "site_settings__main_logo", "language__code").filter(language__code="en").first()
        return ts
    
    def resolve_privacy_notice(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.only('privacy_notice').filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.only('privacy_notice').filter(language__code="en").first()
        return ts.privacy_notice

    def resolve_home_seo_text(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.values('home_seo_text').filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.values('home_seo_text').filter(language__code="en").first()
        return ts.get("home_seo_text")
    
    def resolve_about_us_home(root, info, language="en"):
        setting = SiteSettings.objects.first()
        ts = setting.translates.values('about_us_home').filter(language__code=language).first()
        if ts is None:
            ts = setting.translates.values('about_us_home').filter(language__code="en").first()
        return ts.get("about_us_home")

    def resolve_development_stats(self, info, city=None, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        include_city = city is None
        
        if city: city = City.objects.filter(slug=city.replace("city-", ""))
        else: city = City.objects.filter(is_featured=True)
        return {"language": lang, "cities": city, "include_city": include_city}

class Mutation(graphene.ObjectType):
    create_request_callback = RequestCallBackMutation.Field()
    create_contact_us = ContactUsMutation.Field()
    create_subscribe_newsletter = SubscribeNewsletterMutation.Field()
    create_error_log = ErrorLogMutation.Field()
    
schema = graphene.Schema(query=Query, mutation=Mutation)