from math import ceil
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from developer.models import Developer
from developer.schema import DeveloperSort, FilterDeveloperType
from development.models import Development, DevelopmentTranslate
from development.schema import DevelopmentCardType, DevelopmentSort, FilterDevelopmentType
from main.graphene_tools import PageDetail
from main.models import Language
from .models import CityTranslate, CountryTranslate
from django.db.models import Prefetch
from django.db.models import Q, Count
from promise import Promise
from promise.dataloader import DataLoader
from django.conf import settings
from django.core.cache import cache

CACHE_TIME = settings.CACHE_TIME # cache in second


class CountryTranslateType(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    slug = graphene.String()
    property_num = graphene.Int()
    developments = graphene.Field(FilterDevelopmentType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), city=graphene.String(), country=graphene.String(), sort_by=DevelopmentSort())
    development = graphene.List(DevelopmentCardType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), city=graphene.String(), country=graphene.String(), sort_by=DevelopmentSort())
    icon = graphene.String()
        
    def resolve_id(self, info):
        return self['country_id']
    
    def resolve_slug(self, info):
        return f"country-{self['slug']}"
    
    def resolve_icon(self, info):
        return self['country__icon']
    
    def resolve_property_num(self, info):
        return self['property_num']
    
    def resolve_developments(self, info, page=1, page_size=10, featured=None, sort_by=DevelopmentSort.CREATED_AT_DESC):
        cache_key = f"country_developments_country-{self['country_id']}_{page}_{page_size}_{featured}_{sort_by}_{self['language']}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        filters = Q(development__country_id=self['country_id'], language=self['language'], development__is_active=True, development__is_deleted=False)
        if featured is not None:
            filters &= Q(development__is_featured=featured)
        
        qs = DevelopmentTranslate.objects.select_related(
            'language',
            'development',
            'development__city',
            'development__country',
            'development__default_currency'
        ).prefetch_related(
            'development__flats',
            'development__attachments',
            'development__public_facilities'
        ).filter(filters).order_by("development__is_sold_out", "development__completed_date", sort_by.value)
        qs = qs.distinct()
        count = qs.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
        out = {
            "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, out, CACHE_TIME)
        return out
    
    def resolve_development(self, info, page=1, page_size=10, featured=None, sort_by=DevelopmentSort.CREATED_AT_DESC):
        cache_key = f"country_development_country-{self['country_id']}_{page}_{page_size}_{featured}_{sort_by}_{self['language']}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        filters = Q(development__country_id=self['country_id'], language=self['language'], development__is_active=True, development__is_deleted=False)
        if featured is not None:
            filters &= Q(development__is_featured=featured)
        
        qs = DevelopmentTranslate.objects.select_related(
            'language',
            'development',
            'development__city',
            'development__country',
            'development__default_currency'
        ).prefetch_related(
            'development__flats',
            'development__attachments',
            'development__public_facilities'
        ).filter(filters).order_by("development__is_sold_out", "development__completed_date", sort_by.value)
        qs = qs.distinct()
        count = qs.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
        out = qs[start:end]
        cache.set(cache_key, out, CACHE_TIME)
        return out
    

class CountryFilterType(graphene.ObjectType):
    node = graphene.List(CountryTranslateType)
    page_info = graphene.Field(PageDetail)

class CityTranslateType(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    slug = graphene.String()
    property_num = graphene.Int()
    developments = graphene.Field(FilterDevelopmentType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), sort_by=DevelopmentSort())
    development = graphene.List(DevelopmentCardType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), sort_by=DevelopmentSort())
    icon = graphene.String()

    def resolve_id(self, info):
        return self['city_id']
    
    def resolve_slug(self, info):
        return f"city-{self['slug']}"
    
    def resolve_icon(self, info):
        return self['city__icon']
    
    def resolve_property_num(self, info):
        return self['property_num']
    
    
    def resolve_developments(self, info, page=1, page_size=10, featured=None, sort_by=DevelopmentSort.CREATED_AT_DESC):
        cache_key = f"city_developments_city-{self['city_id']}_{page}_{page_size}_{featured}_{sort_by}_{self['language']}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        filters = Q(development__city_id=self['city_id'], language=self['language'], development__is_active=True, development__is_deleted=False)
        if featured is not None:
            filters &= Q(development__is_featured=featured)
        
        qs = DevelopmentTranslate.objects.select_related(
            'language',
            'development',
            'development__city',
            'development__default_currency'
        ).prefetch_related(
            'development__flats',
            'development__attachments',
            'development__public_facilities'
        ).filter(filters).order_by("development__is_sold_out", "development__completed_date", sort_by.value)
        qs = qs.distinct()
        count = qs.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
        out = {
            "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, out, CACHE_TIME)
        return out
    
    def resolve_development(self, info, page=1, page_size=10, featured=None, sort_by=DevelopmentSort.CREATED_AT_DESC):
        cache_key = f"city_development_city-{self['city_id']}_{page}_{page_size}_{featured}_{sort_by}_{self['language']}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        filters = Q(development__city_id=self['city_id'], language=self['language'], development__is_active=True, development__is_deleted=False)
        if featured is not None:
            filters &= Q(development__is_featured=featured)
        
        qs = DevelopmentTranslate.objects.select_related(
            'language',
            'development',
            'development__city',
            'development__country',
            'development__default_currency'
        ).prefetch_related(
            'development__flats',
            'development__attachments',
            'development__public_facilities'
        ).filter(filters).order_by("development__is_sold_out", "development__completed_date", sort_by.value)
        qs = qs.distinct()
        count = qs.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
        out = qs[start:end]
        cache.set(cache_key, out, CACHE_TIME)
        return out
    
class CityFilterType(graphene.ObjectType):
    node = graphene.List(CityTranslateType)
    page_info = graphene.Field(PageDetail)
    
        
class Query(graphene.ObjectType):
    countries = graphene.Field(CountryFilterType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), language=graphene.String())
    cities = graphene.Field(CityFilterType, page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), language=graphene.String())
    country = graphene.Field(CountryTranslateType, slug=graphene.String(), language=graphene.String())
    country_by_code = graphene.Field(CountryTranslateType, code=graphene.String(), language=graphene.String())
    city = graphene.Field(CityTranslateType, slug=graphene.String(), language=graphene.String())
    

    def resolve_countries(root, info, page=1, page_size=10, search=None, featured=None, language="en"):
        cache_key = f"countries_query_{page}_{page_size}_{search}_{featured}_{language}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        filters = Q(language=lang)
        if search is not None and search != "":
            filters &= Q(country__name__icontains=search) | Q(country__translates__name__icontains=search)
        if featured is not None:
            filters &= Q(country__is_featured=featured)
        qs = CountryTranslate.objects.select_related('language', 'country').annotate(property_num=Count('country__developments')).values("country_id", "name", "slug", "property_num", "country__icon", 'language').filter(filters)
        
        count = qs.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
        out = {
            "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, out, CACHE_TIME)
        return out
    
    def resolve_cities(root, info, page=1, page_size=10, search=None, featured=None, language="en"):
        cache_key = f"city_query_{page}_{page_size}_{search}_{featured}_{language}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 20:
            page_size = 20
        start = 0 + (page - 1) * page_size
        end = start + page_size
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        filters = Q(language=lang)
        if search is not None and search != "":
            filters &= Q(city__name__icontains=search) | Q(city__translates__name__icontains=search)
        if featured is not None:
            filters &= Q(city__is_featured=featured)
        qs = CityTranslate.objects.select_related('language', 'city', 'city__country').annotate(property_num=Count('city__developments')).values("city_id", "name", "slug", "property_num", "city__icon", 'city__country__slug', 'language').filter(filters)
        count = qs.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
        out = {
            "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, out, CACHE_TIME)
        return out
    
    def resolve_country(root, info, slug, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        slug = slug.replace("country-", "",1)
        return CountryTranslate.objects.select_related('language', 'country').annotate(property_num=Count('country__developments')).values("country_id", "name", "slug", "property_num", "country__icon", 'language').filter(country__slug=slug, language=lang).first()
    
    def resolve_country_by_code(root, info, code, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        return CountryTranslate.objects.filter(country__code=code, language=lang).first()
    
    def resolve_city(root, info, slug, language="en"):
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        slug = slug.replace("city-", "",1)
        city = CityTranslate.objects.select_related('language', 'city', 'city__country').annotate(property_num=Count('city__developments')).values("city_id", "name", "slug", "property_num", "city__icon", 'city__country__slug', 'language').filter(city__slug=slug, language=lang).first()
        return city
    
    
schema = graphene.Schema(query=Query)