import datetime
import json
from math import ceil
import time
import graphene
from graphene_django import DjangoObjectType
from random import SystemRandom
from development.utils import flat_bedroom_type
from geo_location.models import City, Country, Demographic, PriceGrowth, PublicFacilityType
from main.graphene_tools import PageDetail
from main.models import Language, SettingTranslate, SiteSettings
from django.db.models import Prefetch
from .models import Attachment, Currency, Development, DevelopmentPublicFacility, DevelopmentTranslate, FaqCategory, FaqCategoryTranslate, FaqTranslate, Feature, FilterSeoText, Flat, FlatPriceHistory, FlatTranslate, ONHArea, ONHDistrict, ONHPostcode, ONHStation, ONHZone, PaymentOption, PaymentPlan
from django.db.models import Q, Min, Max, Avg
from urllib.parse import urlparse, parse_qs
from django.core.cache import cache
from django.contrib.gis.geos import GEOSGeometry, Polygon
from functools import wraps
from bs4 import BeautifulSoup


def cached_resolver(timeout=60*15):
    def decorator(func):
        @wraps(func)
        def wrapper(root, info, **kwargs):
            # Create unique cache key based on query parameters
            cache_key = f"{func.__name__}_{hash(frozenset(kwargs.items()))}"
            result = cache.get(cache_key)

            if result is None:
                result = func(root, info, **kwargs)
                cache.set(cache_key, result, timeout)

            return result
        return wrapper
    return decorator


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 CurrencyType(DjangoObjectType):
    gbp_rate = graphene.Float()

    class Meta:
        model = Currency
        fields = ["id", "name", "code", "demonym", "major_single", "major_plural",
                  "iso_num", "symbol", 'symbol_native', "minor_single", "minor_plural",
                  "iso_digits", "decimal_digits", "num_to_basic", "gbp_rate"]

    def resolve_gbp_rate(self, info):
        try:
            return self.to_currency.filter(from_currency=Currency.objects.get(code="GBP")).first().rate
        except:
            return None


class FilterCurrencyType(graphene.ObjectType):
    node = graphene.List(CurrencyType)
    page_info = graphene.Field(PageDetail)


class AutoScoreNode(graphene.ObjectType):
    name = graphene.String()
    icon = graphene.String()
    rank = graphene.Float()


class AttachmentType(DjangoObjectType):
    type = graphene.String()
    category = graphene.String()

    class Meta:
        model = Attachment
        fields = ['id', 'file', 'image', 'title', 'alt', 'type', 'category']

    def resolve_type(root, info):
        return root.get_type_display()

    def resolve_category(root, info):
        return root.get_category_display()


class CoordinatesType(graphene.ObjectType):
    lat = graphene.Float()
    lon = graphene.Float()


class DevelopmentPublicFacilityType(DjangoObjectType):
    name = graphene.String()
    slug = graphene.String()
    type = graphene.String()
    duration_walk = graphene.Int()
    duration_car = graphene.Int()
    coordinates = graphene.Field(CoordinatesType)

    class Meta:
        model = DevelopmentPublicFacility
        fields = ['id', 'distance', 'duration', 'duration_walk',
                  'duration_car', 'rank', "name", 'type', 'coordinates']

    def resolve_name(self, info):
        try:
            return self.public_facility.name
        except:
            return ""

    def resolve_slug(self, info):
        prefix = ""
        pf = self.public_facility
        if pf.type.slug in ["subway-stations", "train-stations", "bus-stations"]:
            prefix = "station-"
        elif pf.type.slug in ["schools"]:
            prefix = "school-"
        elif pf.type.slug in ["airports"]:
            prefix = "airport-"
        elif pf.type.slug in ["restaurants"]:
            prefix = "restaurant-"
        elif pf.type.slug in ["hospitals"]:
            prefix = "hospital-"
        elif pf.type.slug in ["banks"]:
            prefix = "bank-"
        elif pf.type.slug in ["cinemas"]:
            prefix = "cinema-"
        elif pf.type.slug in ["markets", "supermarkets"]:
            prefix = "market-"
        elif pf.type.slug in ["cafes"]:
            prefix = "cafe-"
        elif pf.type.slug in ["bars"]:
            prefix = "bar-"
        elif pf.type.slug in ["malls"]:
            prefix = "mall-"
        elif pf.type.slug in ["parks"]:
            prefix = "park-"
        elif pf.type.slug in ["theaters"]:
            prefix = "theater-"
        elif pf.type.slug in ["pharmacies"]:
            prefix = "pharmacy-"
        elif pf.type.slug in ["parking"]:
            prefix = "parking"
        elif pf.type.slug in ["gym"]:
            prefix = "gym"
        try:
            return f"{prefix}{self.public_facility.slug}"
        except:
            return ""

    def resolve_coordinates(self, info):
        try:
            return CoordinatesType(self.public_facility.coordinates.y, self.public_facility.coordinates.x)
        except:
            return None

    def resolve_type(self, info):
        try:
            return self.public_facility.type.name
        except:
            return ""

    def resolve_duration_car(self, info):
        try:
            return int(self.duration) if self.public_facility.type_id == 5 else int(self.duration/9.125)
        except:
            return None

    def resolve_duration_walk(self, info):
        try:
            return int(self.duration) if self.public_facility.type_id != 5 else int(self.duration*9.125)
        except:
            return None


class PFType(graphene.ObjectType):
    type = graphene.String()
    icon = graphene.String()
    list = graphene.List(DevelopmentPublicFacilityType)


class KeyFeaturesType(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    value = graphene.String()
    icon = graphene.String()

    def resolve_slug(self, info):

        try:
            return self.feature.translates.filter(language=self.language).first().slug
        except:
            return self.feature.slug

    def resolve_name(self, info):
        try:
            return self.feature.translates.filter(language=self.language).first().name
        except:
            return self.feature.name

    def resolve_icon(self, info):
        return self.feature.icon


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


class PriceGrowthType(graphene.ObjectType):
    postcode = graphene.String()
    date = graphene.Date()
    price = graphene.Float()
    price_change = graphene.Float()
    change_percent = graphene.Float()


class SocialGradeType(graphene.ObjectType):
    name = graphene.String()
    value = graphene.String()


class AgeType(graphene.ObjectType):
    name = graphene.String()
    value = graphene.String()


class CommuteMethodType(graphene.ObjectType):
    name = graphene.String()
    value = graphene.String()


class DemographicType(graphene.ObjectType):
    postcode = graphene.String()
    deprivation = graphene.Float()
    health = graphene.Float()
    social_grade = graphene.List(SocialGradeType)
    age = graphene.List(AgeType)
    proportion_with_degree = graphene.Float()
    vehicles_per_household = graphene.Float()
    commute_method = graphene.List(CommuteMethodType)


class PriceHistoryType(graphene.ObjectType):
    time = graphene.Float()
    price = graphene.Float()


class FlatsType(graphene.ObjectType):
    name = graphene.String()
    is_sold_out = graphene.Boolean()
    area = graphene.String()
    price_per_meter = graphene.String()
    base_price = graphene.String()
    bedrooms_num = graphene.String()
    price_history = graphene.List(PriceHistoryType)


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


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


class PaymentOptionType(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    active = graphene.Boolean()


class PaymentPlanType(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    percent = graphene.Float()


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


class DevelopmentType(graphene.ObjectType):
    title = graphene.String()
    slug = graphene.String()

    def resolve_slug(self, info):
        return f"property-{self.slug}"


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

    def resolve_slug(self, info):
        return f"developer-{self.slug}"


class MapDeveloperNode(graphene.ObjectType):
    name = graphene.String()
    slug = graphene.String()
    image = graphene.String()


class MapFlatsType(graphene.ObjectType):
    name = graphene.String()
    is_sold_out = graphene.Boolean()
    area = graphene.String()
    price_per_meter = graphene.String()
    base_price = graphene.String()
    bedrooms_num = graphene.String()


class MapDevelopmentType(DjangoObjectType):

    class Meta:
        model = Development
        fields = ["geo_json"]


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 PriceType(graphene.ObjectType):
    min = graphene.Float()
    max = graphene.Float()


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


class FAQType(graphene.ObjectType):
    question = graphene.String()
    answer = graphene.String()
    category = graphene.String()
    
    def resolve_category(self, info):
        cat = self.faq.category
        cat_t = cat.translates.filter(language=self.language).first()
        if cat_t:
            return cat_t.title
        cat_t = cat.translates.filter(language__code="en").first()
        if cat_t:
            return cat_t.title
        return ""


class FAQCategoryType(graphene.ObjectType):
    title = graphene.String()
    faqs = graphene.List(FAQType)


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


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

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


class DevelopmentCardType(DjangoObjectType):
    area = graphene.String()
    developer = graphene.String()
    reference_id = graphene.String()
    bedrooms_num = graphene.String()
    currency = graphene.String()
    image = graphene.String()
    is_ready_to_move = graphene.Boolean()
    is_sold_out = graphene.Boolean()
    max_area = graphene.String()
    max_price = graphene.String()
    min_area = graphene.String()
    min_price = graphene.String()
    station = graphene.List(StationNode)
    

    class Meta:
        model = DevelopmentTranslate
        fields = ["id", "reference_id", "title", "slug", 'area', 'min_area', 'max_area', "address", 
                  "bedrooms_num", "currency", "is_sold_out", "image", "min_price", "max_price", 
                  'is_ready_to_move', "station"]

    def resolve_id(self, info):
        return self.development.id
    
    def resolve_id(self, info):
        return self.development.developer.name

    def resolve_reference_id(self, info):
        return self.development.reference_id

    def resolve_slug(self, info):
        return f"property-{self.development.slug}"

    def resolve_title(self, info):
        return self.development.title

    def resolve_is_sold_out(self, info):
        return self.development.flats.all().exclude(base_price=0).count() == 0

    def resolve_area(self, info):
        try:
            return f"{self.development.area:,.0f}"
        except:
            return "0"

    def resolve_min_area(self, info):
        try:
            return f"{self.development.min_area:,.0f}"
        except:
            return "0"

    def resolve_max_area(self, info):
        try:
            return f"{self.development.max_area:,.0f}"
        except:
            return "0"

    def resolve_currency(self, info):
        try:
            s = self.development.default_currency.symbol
        except:
            s = "£"
        return s

    def resolve_min_price(self, info):
        try:
            return f"{self.development.flats.all().exclude(base_price=0).aggregate(Min('base_price'))['base_price__min']:,.0f}"
        except:
            return "0"

    def resolve_max_price(self, info):
        try:
            return f"{self.development.flats.all().exclude(base_price=0).aggregate(Max('base_price'))['base_price__max']:,.0f}"
        except:
            return "0"

    def resolve_bedrooms_num(self, info):
        num = []
        for flat in self.development.flats.all():
            num.append(flat.bedrooms_num)
        num.sort()
        return ",".join([f"{item if item > 0 and item < 10000 else flat_bedroom_type(item, lang=self.language.code)}" for item in num])

    def resolve_is_ready_to_move(self, info):
        return self.development.is_ready_to_move

    def resolve_is_sold_out(self, info):
        return self.development.is_sold_out

    def resolve_image(self, info):
        try:
            return self.development.image
        except:
            return ""
        
    def resolve_station(self, info):
        return ONHStation.objects.filter(developments=self.development).values('name', 'slug', 'icon', 'city__slug').order_by("slug").distinct("slug")[:3]


class DevelopmentTranslateType(DjangoObjectType):
    bedrooms_num = graphene.String()
    bedrooms = graphene.List(BedroomType)
    reference_id = graphene.String()
    min_price = graphene.String()
    max_price = graphene.String()
    price_number = graphene.Field(PriceType)
    completion_date = graphene.String()
    currency = graphene.String()
    is_featured = graphene.Boolean()
    is_highlighted = graphene.Boolean()
    is_ready_to_move = graphene.Boolean()
    is_sold_out = graphene.Boolean()
    area = graphene.String()
    min_area = graphene.String()
    max_area = graphene.String()
    rate = graphene.String()
    floor = graphene.Int()
    completed_date = graphene.Field(CompletionDateNode)
    completed_at = graphene.Date()
    completed_status = graphene.String()
    image = graphene.String()
    location = graphene.Field(CoordinatesType)
    attachment = graphene.List(
        AttachmentType, include=graphene.String(), exclude=graphene.String())
    public_facility = graphene.List(DevelopmentPublicFacilityType)
    public_facility_type = graphene.List(PFType)
    key_features = graphene.List(KeyFeaturesType)
    image_hq = graphene.String()
    flats = graphene.List(FlatsType)
    city = graphene.Field(CityType)
    payment_options = graphene.List(PaymentOptionType)
    payment_plans = graphene.List(PaymentPlanType)
    building_types = graphene.List(BuildingTypeType)
    postcode_growth_price = graphene.List(PriceGrowthType)
    postcode_demographic = graphene.Field(DemographicType)
    similar_developments = graphene.List(DevelopmentCardType)
    train_station = graphene.List(StationNode)
    bus_station = graphene.List(StationNode)
    station = graphene.List(StationNode)
    auto_rank = graphene.List(AutoScoreNode)
    district = graphene.Field(DistrictNode)
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    faq = graphene.List(FAQCategoryType)
    faq_list = graphene.List(FAQType)
    developer = graphene.Field(DeveloperTranslateType)

    class Meta:
        model = DevelopmentTranslate
        fields = ["id", "reference_id", "title", "slug", 'area', 'min_area', 'max_area', "address", "neighborhood",
                  'floor', "is_featured", "is_highlighted", "created_at", "bedrooms_num", "currency", "is_sold_out",
                  "completed_date", "completed_at", "completed_status", "completion_date", "image", "image_hq", "min_price", "max_price",
                  "summary", "description", "features_description", 'is_ready_to_move', "rate", "developer", "location", 'attachment',
                  "public_facility", "key_features", 'flats', 'city', "payment_options", "payment_plans", "postcode_growth_price", "auto_rank",
                  "postcode_demographic", "train_station", "bus_station", "station", "similar_developments", "public_facility_type", "price_number", "district"]

    def resolve_id(self, info):
        return self.development.id

    def resolve_reference_id(self, info):
        return self.development.reference_id

    def resolve_slug(self, info):
        return f"property-{self.development.slug}"

    def resolve_title(self, info):
        return self.development.title

    def resolve_summary(self, info):
        try:
            if self.summary:
                return f'{self.summary[:497]}{"..." if len(self.summary) > 497 else ""}'
            return f'{self.description[:500]}{"..." if len(self.description) > 497 else ""}' if self.description else ""
        except:
            return ""

    def resolve_attachment(self, info, include=None, exclude=None):
        attachments = self.development.attachments.all()
        if include:
            return attachments.filter(category__in=include.split(','))
        elif exclude:
            return attachments.exclude(category__in=exclude.split(','))
        else:
            return attachments

    def resolve_public_facility(self, info):
        return self.development.public_facilities.all()

    def resolve_public_facility_type(self, info):
        # return (
        #     PublicFacilityType.objects.prefetch_related(
        #         'facilities',
        #         'facilities__developments'
        #     ).filter(
        #         facilities__developments__development=self.development
        #     ).distinct()
        # )
        out = []
        for pf_type in PublicFacilityType.objects.all():
            pf = self.development.public_facilities.filter(
                public_facility__type=pf_type)
            if pf.exists():
                out.append(PFType(type=pf_type.name,
                           icon=pf_type.icon, list=pf))
        return out

    def resolve_location(self, info):
        try:
            return CoordinatesType(self.development.coordinates.y, self.development.coordinates.x)
        except:
            return CoordinatesType(51.50522, -0.11192)

    def resolve_district(self, info):
        district = self.development.onh_district.all().first()
        try:
            return {"name": district.name, "slug": f"district-{district.slug}"}
        except:
            return {"name": None, "slug": None}

    def resolve_is_sold_out(self, info):
        return self.development.flats.all().exclude(base_price=0).count() == 0

    def resolve_rate(self, info):
        try:
            f"{self.development.development_reviews.all().aggregate(rate_avg=Avg('rate'))['rate_avg']:,.0f}"
        except:
            crypto_gen = SystemRandom()
            rate = crypto_gen.randrange(3) + 8
            return f"{rate}"

    def resolve_completion_date(self, info):
        if self.development.is_ready_to_move:
            translates = {
                "en": "Ready To Move",
                "fa": "آماده سکونت",
                "ar": "جاهزة للسكن",
                "ru": "Готовая к переезду",
            }
            return translates[self.language.code]
        else:
            return self.development.completed_date

    def resolve_completed_at(self, info):
        return self.development.completed_at

    def resolve_completed_date(self, info):
        translates = {
            "en": "Ready To Move",
            "fa": "آماده سکونت",
            "ar": "جاهزة للسكن",
            "ru": "Готовая к переезду",
        }
        if self.development.is_ready_to_move:
            slug = "completion-ready-to-move"
            name = translates[self.language.code]
        elif self.development.completed_at is not None:
            slug = f"completion-{self.development.completed_at.strftime('%Y')}"
            name = self.development.completed_date
        else:
            slug = "completion-ready-to-move"
            name = translates[self.language.code]
        return {"name": name, "slug": slug}

    def resolve_area(self, info):
        try:
            return f"{self.development.area:,.0f}"
        except:
            return "0"

    def resolve_min_area(self, info):
        try:
            return f"{self.development.min_area:,.0f}"
        except:
            return "0"

    def resolve_max_area(self, info):
        try:
            return f"{self.development.max_area:,.0f}"
        except:
            return "0"

    def resolve_floor(self, info):
        try:
            return f"{self.development.floor:,.0f}"
        except:
            return "0"

    def resolve_currency(self, info):
        try:
            s = self.development.default_currency.symbol
        except:
            s = "£"
        return s

    def resolve_min_price(self, info):
        try:
            return f"{self.development.flats.all().exclude(base_price=0).aggregate(Min('base_price'))['base_price__min']:,.0f}"
        except:
            return "0"

    def resolve_max_price(self, info):
        try:
            return f"{self.development.flats.all().exclude(base_price=0).aggregate(Max('base_price'))['base_price__max']:,.0f}"
        except:
            return "0"

    def resolve_price_number(self, info):
        p = self.development.flats.all().exclude(
            base_price=0).aggregate(Max('base_price'), Min('base_price'))
        return PriceType(min=p['base_price__min'], max=p['base_price__max'])

    def resolve_bedrooms_num(self, info):
        num = []
        for flat in self.development.flats.all():
            num.append(flat.bedrooms_num)
        num.sort()
        return ",".join([f"{item if item > 0 and item < 10000 else flat_bedroom_type(item, lang=self.language.code)}" for item in num])

    def resolve_bedrooms(self, info):
        num = []
        for flat in self.development.flats.all():
            num.append(flat.bedrooms_num)
        num.sort()
        return [{"slug": f"bedroom-{item}", "name": f"{item if item > 0 and item < 10000 else flat_bedroom_type(item, lang=self.language.code)}"} for item in num]

    def resolve_completed_status(self, info):
        return self.development.completed_status

    def resolve_is_featured(self, info):
        return self.development.is_featured

    def resolve_is_highlighted(self, info):
        return self.development.is_highlighted

    def resolve_is_ready_to_move(self, info):
        return self.development.is_ready_to_move

    def resolve_is_sold_out(self, info):
        return self.development.is_sold_out

    def resolve_image_hq(self, info):
        try:
            attachment = self.development.attachments.all().filter(
                type__in=[0, 1]).order_by('?').first()
            return attachment.image
        except:
            try:
                return self.development.image
            except:
                return ""

    def resolve_image(self, info):
        try:
            return self.development.image
        except:
            return ""

    def resolve_key_features(self, info):
        return self.development.key_features.select_related(
            'feature'
        ).prefetch_related(
            'feature__translates'
        ).all()

    def resolve_flats(self, info):
        out = []
        flats = self.development.flats.all()
        now = int(time.time()*1000)

        for flat in flats:
            phs = flat.price_history.all()
            ph_a = []
            try:
                name = flat.translates.filter(
                    language=self.language).first().name
            except:
                name = flat_bedroom_type(
                    flat.bedrooms_num, lang=self.language.code)
            l_price = None
            for ph in phs:
                l_price = ph.price
                p_time = datetime.datetime.fromtimestamp(ph.time/1000)
                p_time = datetime.datetime(
                    p_time.year, p_time.month, p_time.day, 0, 0, 0, 0).timestamp()
                ph_a.append({"time": int(p_time*1000), "price": ph.price})
            if l_price is not None:
                ph_a.append({"time": now, "price": l_price})

            num = flat.bedrooms_num if 0 < flat.bedrooms_num < 10000 else flat_bedroom_type(
                flat.bedrooms_num, lang=self.language.code)
            p_p_m = None
            b_p = None
            if flat.price_per_meter is not None and flat.price_per_meter > 0:
                p_p_m = f"{flat.price_per_meter:,.0f}"
            if flat.base_price is not None and flat.base_price > 0:
                b_p = f"{flat.base_price:,.0f}"
            out.append(FlatsType(name, flat.is_sold_out, f"{flat.area:,.0f}", p_p_m, b_p, num, ph_a))

        return out

    def resolve_city(self, info):
        try:
            city = self.development.city
            ct = city.translates.filter(language=self.language).first()
            if ct is None:
                ct = city
            return {"name": ct.name, "slug": f"city-{city.slug}"}
        except:
            return None

    def resolve_payment_options(self, info):
        out = []
        payment_options = PaymentOption.objects.all()
        for po in payment_options:
            try:
                name = po.translates.filter(
                    language=self.language).first().name
            except:
                name = po.name
            active = po in self.development.payment_option.all()
            out.append(PaymentOptionType(name, f"pay_opt-{po.slug}", active))
        return out

    def resolve_payment_plans(self, info):
        out = []
        payment_plans = PaymentPlan.objects.filter(
            development=self.development)
        for po in payment_plans:
            try:
                title = po.translates.filter(
                    language=self.language).first().title
            except:
                title = po.title
            out.append(PaymentPlanType(
                title, f"pay_plan-{po.slug}", po.percent))
        return out

    def resolve_building_types(self, info):
        out = []
        building_type = self.development.building_type.all()
        for po in building_type:
            try:
                name = po.translates.filter(
                    language=self.language).first().name
            except:
                name = po.name
            out.append(BuildingTypeType(name, po.slug))
        return out

    def resolve_postcode_growth_price(self, info):
        out = []
        try:
            zip_code = self.development.location.zip_code
            postcode = zip_code.split(" ")[0]
        except:
            postcode = None
        pgs = PriceGrowth.objects.filter(
            country=self.development.country, postcode=postcode)
        for pg in pgs:
            out.append(PriceGrowthType(postcode, pg.date, pg.price,
                       pg.price_change, pg.change_percent))
        return out

    def resolve_postcode_demographic(self, info):
        try:
            zip_code = self.development.location.zip_code
            postcode = zip_code.split(" ")[0]
        except:
            postcode = None
        try:
            pgs = Demographic.objects.get(
                country=self.development.country, postcode=postcode)
            social_grade = pgs.social_grade
            sg = []
            for k in social_grade:
                sg.append(SocialGradeType(k, social_grade[k]))
            age = pgs.age
            age_14 = 0
            try:
                age_14 += float(age["0-4"])
            except:
                pass
            try:
                age_14 += float(age["5-9"])
            except:
                pass
            try:
                age_14 += float(age["10-14"])
            except:
                pass

            age_24 = 0
            try:
                age_24 += float(age["15-19"])
            except:
                pass
            try:
                age_24 += float(age["20-24"])
            except:
                pass

            age_44 = 0
            try:
                age_44 += float(age["25-29"])
            except:
                pass
            try:
                age_44 += float(age["30-34"])
            except:
                pass
            try:
                age_44 += float(age["35-39"])
            except:
                pass
            try:
                age_44 += float(age["40-44"])
            except:
                pass

            age_64 = 0
            try:
                age_64 += float(age["45-49"])
            except:
                pass
            try:
                age_64 += float(age["50-54"])
            except:
                pass
            try:
                age_64 += float(age["55-59"])
            except:
                pass
            try:
                age_64 += float(age["60-64"])
            except:
                pass

            age_65 = 0
            try:
                age_65 += float(age["65-69"])
            except:
                pass
            try:
                age_65 += float(age["70-74"])
            except:
                pass
            try:
                age_65 += float(age["10-14"])
            except:
                pass
            try:
                age_65 += float(age["80-84"])
            except:
                pass
            try:
                age_65 += float(age["85-89"])
            except:
                pass
            ag = [
                {
                    "name": "0-14",
                    "value": f"{age_14:,.2f}"
                },
                {
                    "name": "15-24",
                    "value": f"{age_24:,.2f}"
                },
                {
                    "name": "24-44",
                    "value": f"{age_44:,.2f}"
                },
                {
                    "name": "45-64",
                    "value": f"{age_64:,.2f}"
                },
                {
                    "name": "65+",
                    "value": f"{age_65:,.2f}"
                },
            ]
            # for k in age:
            #     ag.append({"name":k, "value":age[k]})

            commute_method = pgs.commute_method
            cm = []
            for k in commute_method:
                cm.append(CommuteMethodType(k.replace("_", " "), commute_method[k]))
            return DemographicType(pgs.postcode, pgs.deprivation, pgs.health, sg, ag, pgs.proportion_with_degree, pgs.vehicles_per_household, cm)
        except:
            return None

    def resolve_similar_developments(self, info):
        return DevelopmentTranslate.objects.select_related(
            'development',
            'development__city',
            'development__country',
            'development__default_currency'
        ).prefetch_related(
            'development__flats',
            'development__attachments'
        ).filter(
            development__developer=self.development.developer,
            development__city=self.development.city,
            language=self.language
        ).exclude(id=self.id).distinct()[:5]

        # developments = DevelopmentTranslate.objects.filter(
        #     development__developer=self.development.developer, development__city=self.development.city, language=self.language).exclude(id=self.id).distinct()
        # if developments.count() < 5:
        #     developments = DevelopmentTranslate.objects.filter(
        #         development__city=self.development.city, language=self.language).exclude(id=self.id).distinct()
        # return developments.order_by("?")[:5]

    def resolve_station(self, info):
        return ONHStation.objects.filter(developments=self.development).values('name', 'slug', 'icon', 'city__slug').order_by("slug").distinct("slug")[:3]

    def resolve_auto_rank(self, info):
        out = []
        ranks = self.development.auto_scores.all()
        for rank in ranks:
            icon = f"{rank.element.icon}"
            name = rank.element.name
            out.append({"name": name, "icon": icon, "rank": rank.rank})
        return out

    def resolve_meta_tags(self, info):
        description = BeautifulSoup(
            self.description, 'html.parser').get_text()[:300]
        return {
            "title": self.title,
            "description": description,
            "keywords": f"new homes, new building, {self.title}, {self.keywords}",
            "og_title": self.title,
            "og_description": description,
            "og_image": f"https://file.entralon.com/{self.development.image}",
            "og_type": "website",
            "twitter_card": "summary",
            "twitter_title": self.title,
            "twitter_description": description,
            "twitter_image": f"https://file.entralon.com/{self.development.image}",
            "canonical_url": f"https://entralon.com/{self.language.code}/property-{self.slug}"
        }

    def resolve_micro_data(self, info):
        return {
            "@context": "https://schema.org",
            "@type": "RealEstateListing",
            "name": self.title,
            "url": f"https://entralon.com/{self.language.code}/property-{self.slug}",
            "image": f"https://file.entralon.com/{self.development.image}",
            "description": self.description,
            "datePosted": self.created_at.isoformat(),
            "mainEntityOfPage": f"https://entralon.com/{self.language.code}/property-{self.slug}",
            "breadcrumb": f"Home > {self.development.city.name} > {self.development.title}",
            "lastReviewed": self.updated_at.isoformat(),
            "specialty": "Real Estate",
            "about": {
                "@type": "Place",
                "name": self.title,
                "address": {
                    "@type": "PostalAddress",
                    "streetAddress": self.address,
                    "addressLocality": self.development.city.name,
                    "addressRegion": self.development.city.name,
                    "postalCode": f"{self.development.onh_postcode.name}",
                    "addressCountry": {
                        "@type": "Country",
                        "name": f"{self.development.country.code}"
                    }
                },
                "geo": {
                    "@type": "GeoCoordinates",
                    "latitude": f"{self.development.coordinates.y}",
                    "longitude": f"{self.development.coordinates.x}"
                }
            },
            "primaryImageOfPage": {
                "@type": "ImageObject",
                "url": f"https://file.entralon.com/{self.development.image}",
                "caption": self.title
            }
        }

    def resolve_faq(self, info):
        try:
            category = FaqCategoryTranslate.objects.select_related(
            'category',
            'language'
            ).filter(
                category__faqs__development_id=self.development.id, language=self.language).distinct()
            if not category.exists():
                category = FaqCategoryTranslate.objects.select_related(
                    'category',
                    'language'
                ).filter(
                    category__faqs__development_id=self.development.id, language__code="en").distinct()
            out = []
            for cat in category:
                faqs = FaqTranslate.objects.select_related(
                        'faq',
                        'language'
                    ).filter(
                    language=cat.language, faq__category=cat.category, faq__development=self.development)
                if not faqs.exists():
                    faqs = FaqTranslate.objects.select_related(
                            'faq',
                            'language'
                        ).filter(
                        faq__category=cat.category, faq__development=self.development, language__code="en")
                out.append({"title": cat.title, "faqs": faqs})
            return out
        except:
            return None

    def resolve_faq_list(self, info):
        try:
            faqs = FaqTranslate.objects.select_related(
                'faq',
                'language'
            ).filter(
                language=self.language, faq__development=self.development)
            if not faqs.exists():
                faqs = FaqTranslate.objects.select_related(
                    'faq',
                    'language'
                ).filter(
                    faq__development=self.development, language__code="en")
            return faqs.order_by('faq__category__order')
        except:
            return None
class FilterDevelopmentType(graphene.ObjectType):
    node = graphene.List(DevelopmentTranslateType)
    page_info = graphene.Field(PageDetail)
    filter_num = graphene.Int()
    meta_tags = graphene.Field(MetaTagsType)
    micro_data = graphene.JSONString()
    seo_text = graphene.String()


class FlatPriceHistoryType(DjangoObjectType):
    class Meta:
        model = FlatPriceHistory
        fields = ["price", "created_at"]


class FlatType(graphene.ObjectType):
    name = graphene.String()
    development = graphene.Field(DevelopmentType)
    developer = graphene.Field(DeveloperType)
    address = graphene.String()
    city = graphene.Field(CityType)
    summary = graphene.String()
    image = graphene.String()
    id = graphene.ID()
    bedrooms_num = graphene.Int()
    bathrooms_num = graphene.Int()
    is_featured = graphene.Boolean()
    is_sold_out = graphene.Boolean()
    area = graphene.Float()
    price_per_meter = graphene.Float()
    base_price = graphene.Float()

    def resolve_development(self, info):
        return self.flat.development

    def resolve_developer(self, info):
        return self.flat.development.developer

    def resolve_address(self, info):
        dev = DevelopmentTranslate.objects.filter(
            development=self.flat.development, language=self.language).first()
        if dev is None:
            dev = DevelopmentTranslate.objects.filter(
                development=self.flat.development, language__code="en").first()
        return dev.address

    def resolve_city(self, info):
        try:
            city = self.development.city
            ct = city.translates.filter(language=self.language).first()
            if ct is None:
                ct = city
            return {"name": ct.name, "slug": f"city-{city.slug}"}
        except:
            return None

    def resolve_summary(self, info):
        try:
            dev = DevelopmentTranslate.objects.filter(
                development=self.flat.development, language=self.language).first()
            if dev is None:
                dev = DevelopmentTranslate.objects.filter(
                    development=self.flat.development, language__code="en").first()
            return f'{dev.summary[:500]}{"..." if len(dev.summary) > 497 else ""}' if dev.summary else ""
        except:
            return ""

    def resolve_image(self, info):
        flat = self.flat
        img_id = flat.bedrooms_num if flat.bedrooms_num < 1000 else 10003 - flat.bedrooms_num
        c = flat.development.attachments.filter(category=2).count()
        c1 = flat.development.attachments.filter(
            category__in=[2, 1, 4, 5, 6]).count()
        c2 = flat.development.attachments.all().count()
        if c > 0:
            if img_id > c-1:
                img_id = 0
            attachment = flat.development.attachments.filter(category=2)[
                img_id]
        elif c1 > 0:
            if img_id > c1-1:
                img_id = 0
            attachment = flat.development.attachments.filter(
                category__in=[2, 1, 4, 5, 6])[img_id]
        elif c2 > 0:
            if img_id > c2-1:
                img_id = 0
            attachment = flat.development.attachments.all()[img_id]
        try:
            return attachment.image
        except:
            return ""

    def resolve_bedrooms_num(self, info):
        return self.flat.bedrooms_num

    def resolve_bathrooms_num(self, info):
        return self.flat.bathrooms_num

    def resolve_is_featured(self, info):
        return self.flat.is_featured

    def resolve_is_sold_out(self, info):
        return self.flat.is_sold_out

    def resolve_area(self, info):
        return self.flat.area

    def resolve_price_per_meter(self, info):
        return self.flat.price_per_meter

    def resolve_base_price(self, info):
        return self.flat.base_price


class FilterFlatType(graphene.ObjectType):
    node = graphene.List(FlatType)
    page_info = graphene.Field(PageDetail)


class DevelopmentSort(graphene.Enum):
    NAME_ASC = "development__title"
    NAME_DESC = "-development__title"
    CREATED_AT_ASC = "created_at"
    CREATED_AT_DESC = "-created_at"
    PRISE_ASC = "development__base_price"
    PRISE_DESC = "-development__base_price"
    RANDOM = "?"


class FlatSort(graphene.Enum):
    BEDROOM_ASC = "flat__bedrooms_num"
    BEDROOM_DESC = "-flat__bedrooms_num"
    AREA_ASC = "flat__area"
    AREA_DESC = "-flat__area"
    CREATED_AT_ASC = "flat__created_at"
    CREATED_AT_DESC = "-flat__created_at"
    PRISE_ASC = "flat__base_price"
    PRISE_DESC = "-flat__base_price"
    RANDOM = "?"


class Query(graphene.ObjectType):
    currencies = graphene.Field(FilterCurrencyType, page=graphene.Int(
    ), page_size=graphene.Int(), search=graphene.String(), language=graphene.String())

    developments = graphene.Field(FilterDevelopmentType, limit=graphene.Boolean(), page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), available=graphene.Boolean(), ready_to_move=graphene.Boolean(),
                                  city=graphene.String(), district=graphene.List(graphene.String), sort_by=DevelopmentSort(), language=graphene.String(), developer=graphene.List(graphene.String),
                                  payment_option=graphene.List(graphene.String), payment_plan=graphene.List(graphene.String), building_type=graphene.List(graphene.String), key_feature=graphene.List(graphene.String), bedroom=graphene.List(graphene.String), bathroom=graphene.List(graphene.String),
                                  london_zip_code=graphene.List(graphene.String), london_area=graphene.List(graphene.String), london_zone=graphene.List(graphene.String), flat=graphene.Boolean(),
                                  completion_date=graphene.List(graphene.String), post_code=graphene.List(graphene.String), region=graphene.List(graphene.String), all_filters=graphene.Boolean(),
                                  station=graphene.List(graphene.String), price_min=graphene.Float(), price_max=graphene.Float(), currency=graphene.String(), polygon=graphene.String())

    flats = graphene.Field(FilterFlatType, limit=graphene.Boolean(), page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), available=graphene.Boolean(), ready_to_move=graphene.Boolean(),
                           city=graphene.String(), district=graphene.List(graphene.String), sort_by=DevelopmentSort(), language=graphene.String(), developer=graphene.List(graphene.String),
                           payment_option=graphene.List(graphene.String), payment_plan=graphene.List(graphene.String), building_type=graphene.List(graphene.String), key_feature=graphene.List(graphene.String), bedroom=graphene.List(graphene.String), bathroom=graphene.List(graphene.String),
                           london_zip_code=graphene.List(graphene.String), london_area=graphene.List(graphene.String), london_zone=graphene.List(graphene.String),
                           completion_date=graphene.List(graphene.String), post_code=graphene.List(graphene.String), region=graphene.List(graphene.String), all_filters=graphene.Boolean(),
                           station=graphene.List(graphene.String), price_min=graphene.Float(), price_max=graphene.Float(), currency=graphene.String())
    development = graphene.Field(DevelopmentTranslateType, slug=graphene.String(
        required=True), language=graphene.String())

    def resolve_currencies(self, info, page=1, page_size=10, search=None, language="en"):
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        qs = Currency.objects.all()
        if search and search != "":
            qs = qs.filter(Q(name__icontains=search) | Q(code__icontains=search) | Q(
                symbol__icontains=search) | Q(symbol_native__icontains=search))
        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_developments(root, info, limit=True, page=1, page_size=10, sort_by=DevelopmentSort.CREATED_AT_DESC, currency=None, language="en", flat=False, **kwargs):
        if page_size > 50:
            page_size = 50
        filter_num = 0
        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")
        f = Q(development__is_active=True, development__is_deleted=False,
              language=lang, development__developer__is_active=True)
        title = setting_t.development_list_title or ""
        description = setting_t.development_list_description or ""
        keywords = "new homes, new building"
        canonical_url = f"https://entralon.com/{lang.code}"
        q = ""
        first_filter_type = None
        first_filter = None
        second_filter_type = None
        second_filter = None
        filter_city = None
        if "featured" in kwargs:
            f &= Q(development__is_featured=kwargs['featured'])
        if "search" in kwargs:
            search = kwargs.get("search")
            search = search.replace("+", " ")
            title = setting_t.development_search_title or ""
            description += f" search for {search}"
            keywords += f", {search}"
            q = f"search={search}"
            s = station_s = district_s = london_zone_s = london_area_s = london_zip_code_s = None
            q = search
            if q is not None and len(q) > 1:
                if s is None:
                    s = Q(development__title__icontains=q) | Q(development__translates__title__icontains=q) | Q(
                        development__slug__icontains=q) | Q(development__translates__slug__icontains=q)
                else:
                    s |= Q(development__title__icontains=q) | Q(development__translates__title__icontains=q) | Q(
                        development__slug__icontains=q) | Q(development__translates__slug__icontains=q)
                if station_s is None:
                    station_s = Q(slug__icontains=q) | Q(name__icontains=q)
                else:
                    station_s |= Q(slug__icontains=q) | Q(name__icontains=q)
                if district_s is None:
                    district_s = Q(slug__icontains=q) | Q(name__icontains=q)
                else:
                    district_s |= Q(slug__icontains=q) | Q(name__icontains=q)
                if london_area_s is None:
                    london_area_s = Q(slug__icontains=q) | Q(name__icontains=q)
                else:
                    london_area_s |= Q(slug__icontains=q) | Q(
                        name__icontains=q)
                if london_zip_code_s is None:
                    london_zip_code_s = Q(
                        slug__icontains=q) | Q(name__icontains=q)
                else:
                    london_zip_code_s |= Q(
                        slug__icontains=q) | Q(name__icontains=q)
                if london_zone_s is None:
                    london_zone_s = Q(slug__icontains=q) | Q(name__icontains=q)
                else:
                    london_zone_s |= Q(slug__icontains=q) | Q(
                        name__icontains=q)
            if station_s is not None:
                s_station = ONHStation.objects.filter(station_s).distinct()
                if s is None:
                    s = Q(development__onh_station__in=s_station)
                else:
                    s |= Q(development__onh_station__in=s_station)
            if district_s is not None:
                s_district = ONHDistrict.objects.filter(district_s).distinct()
                if s is None:
                    s = Q(development__onh_district__in=s_district)
                else:
                    s |= Q(development__onh_district__in=s_district)
            if london_zip_code_s is not None:
                s_london_zip_code = ONHPostcode.objects.filter(
                    london_zip_code_s).distinct()
                if s is None:
                    s = Q(development__onh_postcode__in=s_london_zip_code)
                else:
                    s |= Q(development__onh_postcode__in=s_london_zip_code)
            if london_zone_s is not None:
                s_zone = ONHZone.objects.filter(london_zone_s).distinct()
                if s is None:
                    s = Q(development__onh_zone__in=s_zone)
                else:
                    s |= Q(development__onh_zone__in=s_zone)
            if london_area_s is not None:
                s_area = ONHArea.objects.filter(london_area_s).distinct()
                if s is None:
                    s = Q(development__onh_area__in=s_area)
                else:
                    s |= Q(development__onh_area__in=s_area)

            if s is not None:
                f &= s
        if "available" in kwargs and kwargs['available']:
            filter_num += 1
            is_sold_out = kwargs.get("available") == False
            title += f" available homes"
            description += f"list of available homes"
            keywords += f", available homes"
            q += f"{'&' if q != '' else ''}available=true"
            f &= Q(development__is_sold_out=is_sold_out)
        if "ready_to_move" in kwargs and kwargs['ready_to_move']:
            filter_num += 1
            ready_to_move = kwargs.get("ready_to_move")
            title += f" ready to move homes"
            description += f"list of ready to move homes"
            keywords += f", ready to move homes"
            q += f"{'&' if q != '' else ''}readyToMove=true"
            f &= Q(development__is_ready_to_move=ready_to_move)

        city=None
        default_currency = None
        if "city" in kwargs and kwargs.get("city"):
            # filter_num += 1
            city_k = kwargs.get("city")
            city_slug = city_k.replace("city-", "")
            kwargs['city'] = city_slug
            title += f" in {city_slug}"
            description += f"list of new building in {city_slug}"
            keywords += f", {city_slug}"
            
            canonical_url += f"/city-{city_slug}"

            city = City.objects.filter(slug=city_slug)
            if city.count() > 1:
                city = None
            else:
                city = city.first()
                country = city.country
                default_currency = country.default_currency
            f &= Q(development__city__slug=city_slug)
            filter_city = Q(city__slug=city_slug)
        if "station" in kwargs and kwargs.get("station"):
            filter_num += 1
            station_k = kwargs.get("station")
            station = [item.replace("station-", "", 1) for item in station_k]
            kwargs['station'] = station
            title += f" around of {', '.join(station)} station"
            description += f"list of new building around of {', '.join(station)} station"
            keywords += f", {', '.join(station)}"
            if len(station_k) == 1:
                canonical_url += f"/{station_k[0]}"
                first_filter_type = "station"
                first_filter = station[0]
            else:
                q += f"{'&' if q != '' else ''}station={','.join(station_k)}"
            f &= Q(development__onh_station__slug__in=station)

        if "district" in kwargs and kwargs.get("district"):
            filter_num += 1
            district_k = kwargs.get("district")
            district = [item.replace("district-", "", 1)
                        for item in district_k]
            title += f" in {', '.join(district)} district"
            description += f"list of new building in {', '.join(district)} district"
            keywords += f", {', '.join(district)}"
            if len(district_k) == 1:
                canonical_url += f"/{district_k[0]}"
                if first_filter is None:
                    first_filter_type = "district"
                    first_filter = district[0]
            else:
                q += f"{'&' if q != '' else ''}district={','.join(district_k)}"
            f &= Q(development__onh_district__slug__in=district)
        if "london_zip_code" in kwargs and kwargs.get("london_zip_code"):
            filter_num += 1
            zipcode_k = kwargs['london_zip_code']
            zipcode = [item.replace("zipcode-", "", 1) for item in zipcode_k]
            kwargs['london_zip_code'] = zipcode
            title += f" in {', '.join(zipcode)} zipcode"
            description += f"list of new building in {', '.join(zipcode)} zipcode"
            keywords += f", {', '.join(zipcode)}"
            if len(zipcode_k) == 1:
                canonical_url += f"/{zipcode_k[0]}"
                if first_filter is None:
                    first_filter_type = "postcode"
                    first_filter = zipcode[0]
            else:
                q += f"{'&' if q !='' else ''}londonZipCode={','.join(zipcode_k)}"
            f &= Q(development__onh_postcode__slug__in=zipcode)
        if "key_feature" in kwargs and kwargs.get("key_feature"):
            filter_num += 1
            key_features_k = kwargs.get("key_feature")
            key_features = [item.replace("key_feature-", "", 1)
                            for item in key_features_k]
            title += f" with {', '.join(key_features)} feature"
            description += f"list of new building with {', '.join(key_features)} features"
            keywords += f", {', '.join(key_features)}"
            if len(key_features_k) == 1:
                canonical_url += f"/{key_features_k[0]}"
                if second_filter is None:
                    second_filter_type = "feature"
                    second_filter = key_features[0]
            else:
                q += f"{'&' if q !='' else ''}keyFeature={','.join(key_features_k)}"
            f &= Q(development__key_features__feature__slug__in=key_features)
        if "london_area" in kwargs and kwargs.get("london_area"):
            filter_num += 1
            london_area_k = kwargs.get("london_area")
            london_area = [item.replace("area-", "", 1)
                           for item in london_area_k]
            title += f" in {', '.join(london_area)}"
            description += f"list of new building in {', '.join(london_area)}"
            keywords += f", {', '.join(london_area)}"
            if len(london_area_k) == 1:
                canonical_url += f"/{london_area_k[0]}"
                if first_filter is None:
                    first_filter_type = "area"
                    first_filter = london_area[0]
            else:
                q += f"{'&' if q !='' else ''}londonArea={','.join(london_area_k)}"
            f &= Q(development__onh_area__slug__in=london_area)
        if "london_zone" in kwargs and kwargs.get("london_zone"):
            filter_num += 1
            london_zone = kwargs.get("london_zone")
            title += f" in {', '.join(london_zone)}"
            description += f"list of new building in {', '.join(london_zone)}"
            keywords += f", {', '.join(london_zone)}"
            if len(london_zone) == 1:
                canonical_url += f"/{london_zone[0]}"
                if first_filter is None:
                    first_filter_type = "zone"
                    first_filter = london_zone[0]
            else:
                q += f"{'&' if q !='' else ''}londonZone={','.join(london_zone)}"
            f &= Q(development__onh_zone__slug__in=london_zone)
        if "bedroom" in kwargs and kwargs.get("bedroom"):
            filter_num += 1
            bedrooms_k = kwargs.get("bedroom")
            bedrooms = [item.replace('bedroom-', "", 1) for item in bedrooms_k]
            title += f" with {', '.join([flat_bedroom_type(x, lang.code) for x in bedrooms])}"
            description += f"list of new building with {', '.join([flat_bedroom_type(x, lang.code) for x in bedrooms])}"
            keywords += f", {', '.join([flat_bedroom_type(x, lang.code) for x in bedrooms])}"
            if len(bedrooms_k) == 1:
                canonical_url += f"/{bedrooms_k[0]}"
                if second_filter is None:
                    second_filter_type = "bedroom"
                    second_filter = bedrooms[0]
            else:
                q += f"{'&' if q != '' else ''}bedroom={','.join(bedrooms_k)}"
            f &= Q(development__flats__bedrooms_num__in=bedrooms)
        if "payment_option" in kwargs and kwargs.get("payment_option"):
            filter_num += 1
            payment_options_k = kwargs.get("payment_option")
            payment_options = [item.replace("pay_opt-", "")
                               for item in payment_options_k]
            title += f" with {', '.join(payment_options)} payment option"
            description += f"list of new building with {', '.join(payment_options)} payment option"
            keywords += f", {', '.join(payment_options)}"
            if len(payment_options_k) == 1:
                canonical_url += f"/{payment_options_k[0]}"
            else:
                q += f"{'&' if q !='' else ''}paymentOption={','.join(payment_options_k)}"
            f &= Q(development__payment_option__slug__in=payment_options)
        if "completion_date" in kwargs and kwargs.get("completion_date"):
            filter_num += 1
            completion_date_k = kwargs.get("completion_date")
            completion_date = [item.replace("completion-", "") for item in completion_date_k]
            title += f" complete in {', '.join(completion_date)}"
            description += f"list of new building complete in {', '.join(completion_date)}"
            keywords += f", {', '.join(completion_date)}"
            q += f"{'&' if q !='' else ''}completionDate={','.join(completion_date)}"
            if "ready-to-move" in completion_date:
                completion_date.remove("ready-to-move")
                if len(completion_date) > 0:
                    f &= Q(development__is_ready_to_move=True) | Q(development__completed_at__year__in=completion_date)
                else:
                    f &= Q(development__is_ready_to_move=True)
            else:
                f &= Q(development__completed_at__year__in=completion_date)
        if "price_min" in kwargs and kwargs.get("price_min") and "price_max" in kwargs and kwargs.get("price_max"):
            filter_num += 1
            price_from = kwargs.get("price_min")
            price_to = kwargs.get("price_max")
            title += f" from {price_from:.0f} to {price_to:.0f}{' '+default_currency.symbol if default_currency else ''}"
            description += f"list of new building from {price_from:.0f} to {price_to:.0f}"
            keywords += f", from {price_from:.0f} to {price_to:.0f}"
            q += f"{'&' if q !='' else ''}priceMin={price_from}&priceMax={price_to}"
            f &= Q(development__flats__base_price__range=(price_from, price_to))
        else:
            if "price_min" in kwargs and kwargs.get("price_min"):
                filter_num += 1
                price_from = kwargs.get("price_min")
                title += f" more than {price_from:.0f}{' '+default_currency.symbol if default_currency else ''}"
                description += f"list of new building more than {price_from:.0f}"
                keywords += f", more than {price_from:.0f}"
                q += f"{'&' if q != '' else ''}priceMin={price_from:.0f}"
                f &= Q(development__flats__base_price__gte=price_from)
            if "price_max" in kwargs and kwargs.get("price_max"):
                price_to = kwargs.get("price_max")
                title += f" less than {price_to:.0f}{' '+default_currency.symbol if default_currency else ''}"
                description += f"list of new building less than {price_to:.0f}"
                keywords += f", less than {price_to:.0f}"
                q += f"{'&' if q != '' else ''}priceMax={price_to:.0f}"
                
                f &= Q(development__flats__base_price__gt=0) & Q(development__flats__base_price__lte=price_to)
                
        if "polygon" in kwargs:
            polygon = kwargs.get("polygon")
            # create a polygon from the polygon string and check if the point is inside the polygon
            coordinates = json.loads(polygon)
            if coordinates[0] != coordinates[-1]:
                coordinates.append(coordinates[0])
            polygon_obj = Polygon(coordinates)
            f &= Q(development__coordinates__within=polygon_obj)
        qs = DevelopmentTranslate.objects.only(
            'id',
            'title',
            'slug',
            'summary',
            'development__base_price',
            'development__is_featured',
            'language',
            'developer',
        ).select_related(
            'language',
            'development',
        ).prefetch_related(
            'development__city',
            'development__country',
            'development__default_currency',
            'development__developer',
            'development__flats',
            'development__attachments',
            'development__public_facilities',
            'development__public_facilities__public_facility',
            'development__public_facilities__public_facility__type',
            'development__key_features',
            'development__key_features__feature',
            'development__payment_option',
            'development__payment_plan'
        ).filter(f).distinct()
        if flat:
            qs = qs.order_by("development__is_sold_out",
                             "development__completed_at", sort_by.value)
        else:
            qs = qs.order_by("development__is_sold_out", sort_by.value)
        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
        if page > 1:
            q += f"{'&' if q != '' else ''}page={page}"
            title += f" | Page {page}"
            description += f" | Page {page}"
        if q != "":
            q = "?" + q
        canonical_url += q
        item_list = []
        for item in qs[start:end]:
            item_list.append({
                "@type": "ListItem",
                "position": item.id,
                "url": f"https://entralon.com/{lang.code}/property-{item.development.slug}",
                "name": f"{item.development.title}",
                "image": f"https://file.entralon.com/{item.development.image}",
                "description": item.summary,
                "item": {
                    "@type": "RealEstateListing",
                    "name": f"{item.development.title}",
                    "offers": {
                        "@type": "Offer",
                        "price": "sold out" if item.development.is_sold_out else item.development.base_price,
                        "priceCurrency": item.development.default_currency.code,
                        "availability": "https://schema.org/InStock"
                    }
                }
            })
        micro_data = {
            "@context": "https://schema.org",
            "@type": "ItemList",
            "itemListElement": item_list,
        }

        image = f"https://file.entralon.com/{setting.default_development_image}"
        seo_text = None
        
        if filter_city:
            fst = filter_city
        else:
            fst = Q(city=None)
        f_name = None

        if first_filter:
            try:
                if first_filter_type == "station":
                    f_name = ONHStation.objects.get(slug=first_filter).name
                elif first_filter_type == "district":
                    f_name = ONHDistrict.objects.get(slug=first_filter).name
                elif first_filter_type == "postcode":
                    f_name = ONHPostcode.objects.get(slug=first_filter).name
                elif first_filter_type == "area":
                    f_name = ONHArea.objects.get(slug=first_filter).name
                elif first_filter_type == "zone":
                    f_name = ONHZone.objects.get(slug=first_filter).name
            except:
                f_name = None
                first_filter_type = None
        fst &= Q(first_filter=f_name) & Q(first_filter_type=first_filter_type)
        s_name = None
        if second_filter:
            try:
                if second_filter_type == "bedroom":
                    s_name = second_filter
                elif second_filter_type == "feature":
                    s_name = Feature.objects.get(slug=second_filter).name
            except:
                s_name = None
                second_filter_type = None
        fst &= Q(second_filter=s_name) & Q(
            second_filter_type=second_filter_type)

        filter_seo_text = FilterSeoText.objects.filter(fst).first()
        seo_text_t = None
        if filter_seo_text is None or filter_seo_text.translates.all().count() == 0:
            filter_seo_text = FilterSeoText.objects.filter(
                city=None, first_filter=None, first_filter_type=None, second_filter=None, second_filter_type=None).first()
        if filter_seo_text:
            seo_text_t = filter_seo_text.translates.filter(
                language=lang).first()
            if seo_text_t is None:
                seo_text_t = filter_seo_text.translates.filter(
                    language__code="en").first()
        if seo_text_t:
            seo_text = seo_text_t.text
        return {
            "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,
            },
            "seo_text": seo_text,
            "filter_num": filter_num,
            "city": city,
            "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
            },
            "micro_data": micro_data,

        }

    def resolve_flats(root, info, limit=True, page=1, page_size=10, sort_by=FlatSort.CREATED_AT_DESC, currency="GBP", language="en", all_filters=True, **kwargs):
        if page_size > 50:
            page_size = 50
        filter_num = 0
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        try:
            default_currency = Currency.objects.get(code=currency)
        except:
            default_currency = Currency.objects.get(code="GBP")
        city = None
        f = Q(development__is_active=True, development__is_deleted=False)
        if "featured" in kwargs:
            f &= Q(development__is_featured=kwargs['featured'])
        if "city" in kwargs and kwargs.get("city"):
            # filter_num += 1
            city_slug = kwargs.get("city")
            city_slug = city_slug.replace("city-", "")
            kwargs['city'] = city_slug
            city = City.objects.filter(slug=city_slug)
            if city.count() > 1:
                city = None
            else:
                city = city.first()
            f &= Q(development__city__slug=city_slug)

        if "payment_option" in kwargs and kwargs.get("payment_option"):
            filter_num += 1
            payment_options = kwargs.get("payment_option")
            payment_options = [item.replace("pay_opt-", "")
                               for item in payment_options]
            f &= Q(development__payment_option__slug__in=payment_options)
        if "payment_plan" in kwargs and kwargs.get("payment_plan"):
            filter_num += 1
            payment_plans = kwargs.get("payment_plan")
            payment_plans = [item.replace("pay_plan-", "")
                             for item in payment_plans]
            f &= Q(development__payment_plan__slug__in=payment_plans)
        if "building_type" in kwargs and kwargs.get("building_type"):
            filter_num += 1
            building_types = kwargs.get("building_type")
            f &= Q(development__building_type__slug__in=building_types)
        if "key_feature" in kwargs and kwargs.get("key_feature"):
            filter_num += 1
            key_features = kwargs.get("key_feature")
            key_features = [item.replace("key_feature-", "", 1)
                            for item in key_features]
            f &= Q(development__key_features__feature__slug__in=key_features)
        if "bedroom" in kwargs and kwargs.get("bedroom"):
            filter_num += 1
            bedrooms = kwargs.get("bedroom")
            bedrooms = [item.replace('bedroom-', "", 1) for item in bedrooms]
            f &= Q(bedrooms_num__in=bedrooms)
        if "price_min" in kwargs and kwargs.get("price_min"):
            filter_num += 1
            price_from = kwargs.get("price_min")
            f &= Q(base_price__gt=0) & Q(base_price__gte=price_from)
        if "price_max" in kwargs and kwargs.get("price_max"):
            price_to = kwargs.get("price_max")
            f &= Q(base_price__gt=0) & Q(base_price__lte=price_to)

        if "district" in kwargs and kwargs.get("district"):
            filter_num += 1
            district = kwargs.get("district")
            district = [item.replace("district-", "", 1) for item in district]
            f &= Q(development__onh_district__slug__in=district)
        if "london_zone" in kwargs and kwargs.get("london_zone"):
            filter_num += 1
            london_zone = kwargs.get("london_zone")
            f &= Q(development__location__london_zone__slug__in=london_zone)
        if "london_area" in kwargs and kwargs.get("london_area"):
            filter_num += 1
            london_area = kwargs.get("london_area")
            london_area = [item.replace("area-", "", 1)
                           for item in london_area]
            f &= Q(development__location__london_area__slug__in=london_area)
        if "london_zip_code" in kwargs and kwargs.get("london_zip_code"):
            filter_num += 1
            zipcode = kwargs['london_zip_code']
            zipcode = [item.replace("zipcode-", "", 1) for item in zipcode]
            kwargs['london_zip_code'] = zipcode
            f &= Q(development__onh_postcode__slug__in=zipcode)
        if "completion_date" in kwargs and kwargs.get("completion_date"):
            filter_num += 1
            completion_date = kwargs.get("completion_date")
            completion_date = [item.replace(
                "completion-", "", 1) for item in completion_date]
            if "ready-to-move" in completion_date:
                completion_date.remove("ready-to-move")
                if len(completion_date) > 0:
                    f &= Q(development__is_ready_to_move=True) | Q(development__completed_at__year__in=completion_date)
                else:
                    f &= Q(development__is_ready_to_move=True)
            else:
                f &= Q(development__completed_at__year__in=completion_date)
        if "post_code" in kwargs and kwargs.get("post_code"):
            filter_num += 1
            post_code = kwargs.get("post_code")
            f &= Q(development__location__post_code__out_code__in=post_code)
        if "region" in kwargs and kwargs.get("region"):
            filter_num += 1
            region = kwargs.get("region")
            f &= Q(development__location__post_code__region__slug__in=region)
        if "station" in kwargs and kwargs.get("station"):
            filter_num += 1
            station = kwargs.get("station")
            station = [item.replace("station-", "", 1) for item in station]
            kwargs['station'] = station
            f &= Q(development__onh_station__slug__in=station)
        if "available" in kwargs and kwargs['available']:
            filter_num += 1
            is_sold_out = kwargs.get("available") == False
            f &= Q(development__is_sold_out=is_sold_out)
        if "ready_to_move" in kwargs and kwargs['ready_to_move']:
            filter_num += 1
            ready_to_move = kwargs.get("ready_to_move")
            f &= Q(development__is_ready_to_move=ready_to_move)
        flats = Flat.objects.filter(f)
        qs = FlatTranslate.objects.select_related(
            'flat',
            'flat__development',
            'flat__development__city',
            'flat__development__country',
            'language'
        ).prefetch_related(
            'flat__price_history',
            'flat__development__attachments'
        ).filter(flat__in=flats, language=lang).order_by(
            "flat__is_sold_out", sort_by.value).distinct()
        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

        results = {}
        results["node"] = qs[start:end]
        results["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,
        }
        results['language'] = lang.code
        results["filter_num"] = filter_num
        results["city"] = city

        return results

    @cached_resolver(timeout=60*15)
    def resolve_development(root, info, slug, language="en"):
        lang = Language.objects.filter(code=language).first()
        slug = slug.replace("property-", "")
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        development = DevelopmentTranslate.objects.select_related(
            'development',
            'development__country',
            'development__city',
            'development__default_currency',
            'development__developer',
            'language'
        ).prefetch_related(
            'development__flats',
            'development__flats__price_history',
            'development__flats__translates',
            'development__attachments',
            'development__key_features',
            'development__key_features__feature',
            'development__key_features__feature__translates',
            'development__payment_option',
            'development__payment_option__translates',
            'development__payment_plan',
            'development__payment_plan__translates',
            'development__building_type',
            'development__building_type__translates',
            'development__public_facilities',
            'development__public_facilities__public_facility',
            'development__public_facilities__public_facility__type',
            'development__onh_station',
            'development__auto_scores',
            'development__auto_scores__element'
        ).filter(
            Q(development__slug=slug) | Q(development__translates__slug=slug),
            development__is_active=True,
            development__is_deleted=False,
            development__developer__is_active=True,
            language=lang
        ).first()
        if development is None:
            development = DevelopmentTranslate.objects.select_related(
                'development',
                'development__country',
                'development__city',
                'development__default_currency',
                'language'
            ).prefetch_related(
                'development__flats',
                'development__flats__price_history',
                'development__attachments',
                'development__key_features',
                'development__key_features__feature',
                'development__key_features__feature__translates',
                'development__payment_option',
                'development__payment_plan',
                'development__building_type',
                'development__public_facilities',
                'development__public_facilities__public_facility',
                'development__public_facilities__public_facility__type',
                'development__onh_station',
                'development__auto_scores',
                'development__auto_scores__element'
            ).filter(
                Q(development__slug=slug) | Q(
                    development__translates__slug=slug),
                development__is_active=True,
                development__is_deleted=False,
                language__code="en"
            ).first()
        return development


schema = graphene.Schema(query=Query)
