from math import ceil
import graphene
from graphene_django import DjangoObjectType
from django.db.models import Q
from promise import Promise
from blog.dataloaders import CategoryLoader, KeywordLoader, NewsLoader
from main.models import Language, SiteSettings
from main.graphene_tools import PageDetail
from .models import CategoryTranslate, News, Category, Keyword, NewsTranslate
from django.conf import settings
from django.core.cache import cache

CACHE_TIME = settings.CACHE_TIME # cache in second

class BlogPostMicroData(graphene.ObjectType):
    type = graphene.String(default_value="Article")
    headline = graphene.String()
    image = graphene.String()
    date_published = graphene.String()
    date_modified = graphene.String() 
    author = graphene.String()
    description = graphene.String()
    publisher = graphene.String()
    main_entity_of_page = graphene.String()
    
    def resolve_headline(self, info):
        return self.title
    
    def resolve_image(self, info):
        return f"https://file.entralon.com/{self.news.image}"
    
    def resolve_date_published(self, info):
        return self.news.created_at.isoformat()
    
    def resolve_date_modified(self, info):
        return self.news.updated_at.isoformat()
    
    def resolve_author(self, info):
        return "Entralon"
    
    def resolve_description(self, info):
        return self.summary
    
    def resolve_publisher(self, info):
        return "Entralon"
    
    def resolve_main_entity_of_page(self, info):
        return f"https://entralon.com/{self.language.code}/news/{self.news.slug}"
    
class BlogListingMicroData(graphene.ObjectType):
    type = graphene.String(default_value="ItemList")
    items = graphene.List(BlogPostMicroData)
    number_of_items = graphene.String()
    item_list_order = graphene.String()

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 CategoryType(DjangoObjectType):
    class Meta:
        model = Category
        fields = ["id", "slug", "title"]

class CategoryTranslateType(DjangoObjectType):
    class Meta:
        model = CategoryTranslate
        fields = ["id", "slug", "title"]
        
    def resolve_slug(self,info):
        return self.category.slug

class FilterCategoryType(graphene.ObjectType):
    node = graphene.List(CategoryTranslateType)
    page_info = graphene.Field(PageDetail)


class KeywordType(DjangoObjectType):
    class Meta:
        model = Keyword
        fields = ["id", "slug", "title"]

class FilterKeywordType(graphene.ObjectType):
    node = graphene.List(KeywordType)
    page_info = graphene.Field(PageDetail)
        

class NewsType(DjangoObjectType):
    class Meta:
        model = News
        fields = ["id", "slug", "development", "title", "image", "keywords", "category", "created_at"]

class NewsCardTranslateType(DjangoObjectType):
    image = graphene.String()
    class Meta:
        model = NewsTranslate
        fields = ["id", "slug", "title", "image", 'summary', "description", "keywords", "category", "created_at", "read_time"]

    def resolve_id(self, info):
        return self.news.id
    
    def resolve_slug(self, info):
        return self.news.slug
    
    def resolve_image(self, info):
        return self.news.image
    
    def resolve_summary(self, info):
        return f"{self.summary[:100]} ..."

class NewsTranslateType(DjangoObjectType):
    image = graphene.String()
    related_news = graphene.List(NewsCardTranslateType)
    micro_data = graphene.JSONString()
    meta_tags = graphene.Field(MetaTagsType)
    class Meta:
        model = NewsTranslate
        fields = ["id", "slug", "title", "image", 'summary', "description", "keywords", "category", "created_at", "read_time", "related_news"]

    def resolve_id(self, info):
        return self.news.id
    
    def resolve_slug(self, info):
        return self.news.slug
    
    def resolve_image(self, info):
        return self.news.image
    
    def resolve_summary(self, info):
        return f"{self.summary[:100]} ..."
    
    def resolve_related_news(self, info):
        news = NewsTranslate.objects.select_related(
            'news',
            'language',
            'category'
        ).prefetch_related(
            'keywords'
        ).filter(language=self.language, category=self.category).exclude(id=self.id)
        if news.count() < 5:
            news = NewsTranslate.objects.select_related(
                'news',
                'language',
                'category'
            ).prefetch_related(
                'keywords'
            ).filter(Q(category=self.category) | Q(keywords__in=self.keywords.all()), language=self.language).exclude(id=self.id)
        return news.order_by("?")[:5]
    
    def resolve_micro_data(self, info):
        return {
                "@context": "https://schema.org",
                "@type": "NewsArticle",
                "headline": self.title,
                "url": f"https://dev.entralon.com/{self.language.code}/news/{self.news.slug}",
                "image": f"https://file.entralon.com/{self.news.image}",
                "datePublished": self.created_at.isoformat(),
                "author": {
                    "@type": "Person",
                    "name": "Entralon"
                },
                "publisher": {
                    "@type": "Organization",
                    "name": "Entralon",
                    "logo": {
                    "@type": "ImageObject",
                    "url": "https://file.entralon.com/favicon.ico"
                    }
                },
                "articleBody": self.description
            }


    
    def resolve_meta_tags(self, info):
        keywords = ", ".join([k.title for k in self.keywords.all()])
        base_url = "https://entralon.com"  # Adjust based on your domain
        canonical = f"{base_url}/{self.language.code}/news/{self.news.slug}"
        
        return {
            "title": f"{self.title} | Entralon Blog",
            "description": self.summary[:160],
            "keywords": keywords,
            "og_title": self.title,
            "og_description": self.summary[:160],
            "og_image": f"https://file.entralon.com/{self.news.image}",
            "og_type": "article",
            "twitter_card": "summary_large_image",
            "twitter_title": self.title,
            "twitter_description": self.summary[:160],
            "twitter_image": f"https://file.entralon.com/{self.news.image}",
            "canonical_url": canonical
        }
    
    
class FilterNewsType(graphene.ObjectType):
    node = graphene.List(NewsTranslateType)
    page_info = graphene.Field(PageDetail)
    micro_data = graphene.JSONString()
    meta_tags = graphene.Field(MetaTagsType)
    

    def resolve_micro_data(self, info):
        item_list = []
        for item in self['node']:
            item_list.append({
                "@type": "ListItem",
                "position": 1,
                "url": f"https://dev.entralon.com/{item.language.code}/news/{item.news.slug}",
                "name": item.title,
                "datePublished": item.created_at.isoformat()
                })
        return {
            "@context": "https://schema.org",
            "@type": "ItemList",
            "itemListElement": item_list
        }

        
    


class Query(graphene.ObjectType):
    
    news = graphene.Field(FilterNewsType, language=graphene.String(), page=graphene.Int(), page_size=graphene.Int(), search=graphene.String(), featured=graphene.Boolean(), category=graphene.String(), keyword=graphene.String())
    news_detail = graphene.Field(NewsTranslateType, language=graphene.String(), slug=graphene.String(required=True))
    news_category = graphene.Field(FilterCategoryType, language=graphene.String(), page=graphene.Int(), page_size=graphene.Int(), search=graphene.String())
    news_keyword = graphene.Field(FilterKeywordType, language=graphene.String(), page=graphene.Int(), page_size=graphene.Int(), search=graphene.String())

    @staticmethod
    def get_loaders():
        return {
            'news_loader': NewsLoader(),
            'category_loader': CategoryLoader(),
            'keyword_loader': KeywordLoader()
        }
    
    def resolve_news(self, info, language="en", page=1, page_size=10, search=None, featured=None, category=None, keyword=None):
        cache_key = f"news_query_{page}_{page_size}_{search}_{featured}_{category}_{keyword}_{language}"
        cached_result = cache.get(cache_key)
        if cached_result:
            return cached_result
        if page_size > 50:
            page_size = 50
        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(news__is_publish=True, news__is_deleted=False, language=lang)
        
        if featured is not None:
            filters &= Q(news__is_featured=featured)
        if search is not None and search != "":
            filters &= Q(news__title__icontains=search) | Q(news__translates__title__icontains=search)
        if category is not None and search != "":
            filters &= Q(news__category__slug=category)
        if keyword is not None and search != "":
            filters &= Q(news__keywords__slug=keyword)
        qs = NewsTranslate.objects.select_related(
            'news',
            'language',
            'category',
            'category__category'
        ).prefetch_related(
            'keywords',
            'news__keywords'
        ).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
        canonical_url = f"https://entralon.com/{lang.code}/news"
        title = "Sales & Investment News"
        description = "list of latest Sales & Investment News and updates on Entralon."
        qp = ""
        if search:
            qp += f"{'&' if qp != '' else ''}search={search}"
            title = f"search {search} in News"
            description = f"list of search {search} in latest Sales & Investment News on Entralon."
        if category:
            qp += f"{'&' if qp != '' else ''}category={category}"
            title += f" in {category} category"
            description += f"list of latest Sales & Investment News in {category} category on Entralon."
        if keyword:
            qp += f"{'&' if qp != '' else ''}keyword={keyword}"
            title += f" in {keyword} keyword"
            description += f"list of latest Sales & Investment News in {keyword} keyword on Entralon."
        if page > 1: 
            qp += f"{'&' if qp != '' else ''}page={page}"
            title += f" | Page {page}"
            description += f" | Page {page}"
        if qp != "": qp = "?" + qp
        canonical_url += qp
        title += " | Entralon"
        image = f"https://file.entralon.com/{SiteSettings.objects.first().default_news_image}"
        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,
            },
            "meta_tags": {
                "title": title,
                "description": description,
                "keywords": "blog, news, entralon, updates, new homes, new apartment",
                "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
            }
        }
        cache.set(cache_key, out, CACHE_TIME)
        return out
    
    def resolve_news_detail(self, info, slug, language="en"):
        loaders = NewsLoader()
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        news = NewsTranslate.objects.filter(
            news__slug=slug,
            language=lang,
            news__is_publish=True, 
            news__is_deleted=False
        ).first()
        return loaders.load(news.id).get() if news else None
    

    def resolve_news_category(self, info, language="en", page=1, page_size=10, search=None):
        if page_size > 20:
            page_size = 20
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        start = 0 + (page - 1) * page_size
        end = start + page_size
        qs = CategoryTranslate.objects.filter(category__is_activate=True, language=lang)
        if search and search != "":
            qs = qs.filter(Q(title__icontains=search) | Q(translates__title__icontains=search))
            
        loaders = CategoryLoader()
        promises = [loaders.load(category.id) for category in qs[start:end]]
        return {

            "node": Promise.all(promises).get(),
            "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_news_keyword(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
        lang = Language.objects.filter(code=language).first()
        if lang is None:
            lang = Language.objects.filter(code="en").first()
        qs = Keyword.objects.filter(language=lang)
        if search and search != "":
            qs = qs.filter(Q(title__icontains=search))
        loaders = KeywordLoader()
        promises = [loaders.load(keyword.id) for keyword in qs[start:end]]
        return {

            "node": promises.all(promises).get(),
            "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),
            }

        }



schema = graphene.Schema(query=Query)
