import datetime
import json
import time
from logging import getLogger
from django.conf import settings

import requests
from billiard.exceptions import SoftTimeLimitExceeded
from dateutil import parser
from django.db.models import Subquery
from django.utils import timezone

from verify_trusted.companies.models import Review
from verify_trusted.reviews.models import ReviewSource, Platform
from verify_trusted.widgets.models import WidgetReivews, Widget

logger = getLogger(__name__)
max_crawler = settings.MAX_CRAWLER_SIZE



class CheckatradeCrawler:
    def get_reviews(self, url: str):
        total_reviews = 0
        avg_rating = 0.0
        try:
            logger.debug(
                "====================================checkatrade crawler=============================================")
            company_name = url.split("/")[-1]
            try:
                general_content = requests.get(f"https://trade-profile.checkatrade.com/companies/{company_name}").json()
                total_reviews = general_content['reviews_count']
                avg_rating = general_content['rating']
                page_num = int(requests.get(
                    f"https://trade-profile.checkatrade.com/companies/{company_name}/reviews?page=1&itemsPerPage=18&sortBy=createDate.desc").json()[
                                   "pages"])
            except:
                page_num = 1
            num = 1
            logger.debug(page_num)
            parsed_reviews = []
            while num <= page_num:
                crawl_url = f"https://trade-profile.checkatrade.com/companies/{company_name}/reviews?page={num}&itemsPerPage=18&sortBy=createDate.desc"
                logger.debug(crawl_url)
                res = requests.get(crawl_url)
                reviews = res.json()["items"]
                logger.debug(json.dumps(reviews, indent=2))

                parsed_reviews += [
                    {
                        'id': review['id'],
                        'author': "Reviewer",
                        'date': parser.parse(review['date']),
                        'headline': str(review['title'] or '').strip(),
                        'body': str(review['body'] or '').strip(),
                        'rating': round(review['rating'] / 2, 2),
                    }
                    for review in reviews if review['date'] is not None
                ]
                print(len(parsed_reviews))

                logger.debug(parsed_reviews)
                num += 1
                if len(parsed_reviews) >= max_crawler:
                    parsed_reviews = [k for j, k in enumerate(parsed_reviews) if k not in parsed_reviews[j + 1:]]
                    parsed_reviews = parsed_reviews[:max_crawler]
                    if total_reviews == 0 and avg_rating == 0:
                        total_reviews = None
                        avg_rating = None
                    break
            return total_reviews, avg_rating, parsed_reviews
        except Exception as e:
            print(e)
            return None

    def sync_reviews(self, review_source: ReviewSource, is_add):
        total_reviews, avg_rating, reviews = self.get_reviews(review_source.url)
        if reviews is None:
            review_source.sync_status = ReviewSource.SyncStatus.FALSE
            review_source.save()
            return
        reviews = [
            Review(
                source=review_source,
                author=r['author'],
                date=r['date'],
                headline=r['headline'],
                body=r['body'],
                rating=r['rating'],
                date_parse=timezone.now(),
                external_id=r['id'],
            )
            for r in reviews
        ]
        Review.objects.filter(source_id=review_source.id, lock_edit=True).exclude(id__in=Subquery(
            WidgetReivews.objects.values_list('review_id', flat=True)), display_order__isnull=False).delete()
        Review.objects.bulk_create(reviews, ignore_conflicts=False)  # sua lai khi test xong
        try:

            # get ids of widget comments and review has display_order not null
            dup_reviews_ids = Review.objects.filter(source_id=review_source.id, id__in=Subquery(
                WidgetReivews.objects.values_list('review_id', flat=True)), display_order__isnull=False).values_list(
                'id', flat=True)

            # # check if comment not in widget comment then delete
            uncheck_reviews = Review.objects.filter(id__in=dup_reviews_ids)

            for row in uncheck_reviews:
                reviews = Review.objects.filter(source_id=review_source.id, body=row.body, date=row.date).exclude(
                    id__in=list(dup_reviews_ids))
                try:
                    external_id = reviews[0].external_id
                    row.update(external_id=external_id)
                except:
                    pass
                reviews.delete()
            active_reviews = Review.objects.filter(source_id__in=Subquery(ReviewSource.objects.filter(
                company_id=review_source.company_id).values_list('id', flat=True)), is_active=True).order_by(
                'display_order', '-date_modify', '-date')
            num_active_reviews = len(active_reviews)
            if num_active_reviews < 20:
                nested_q = Review.objects.filter(source_id__in=Subquery(ReviewSource.objects.filter(
                    company_id=review_source.company_id).values_list('id', flat=True)), is_active=False).order_by(
                    'display_order', '-date_modify', '-date')[:(20 - num_active_reviews)]
                Review.objects.filter(pk__in=nested_q).update(is_active=True)
            elif num_active_reviews > 20:
                nested_q = Review.objects.filter(source_id__in=Subquery(ReviewSource.objects.filter(
                    company_id=review_source.company_id).values_list('id', flat=True)),
                                                 is_active=True).order_by('display_order', '-date_modify', '-date')[20:]
                Review.objects.filter(pk__in=nested_q).update(is_active=False, display_order=None)

            if is_add is True:
                widget = Widget.objects.filter(company=review_source.company_id).first()
                widget_reviews = WidgetReivews.objects.filter(widget=widget.id, review__source__platform__status=Platform.Status.ACTIVE).values_list('review', flat=True)
                num_widget_reviews = len(widget_reviews)
                ws = []
                if num_widget_reviews < 5:
                    nested_w = Review.objects.filter(source_id__in=Subquery(ReviewSource.objects.filter(
                        company_id=review_source.company_id,platform__status=Platform.Status.ACTIVE).values_list('id', flat=True))).order_by('display_order', '-date_modify', '-date')[:(5-num_widget_reviews)]
                    for w in nested_w:
                        ws.append(WidgetReivews(widget=widget, review=w))
                    WidgetReivews.objects.bulk_create(ws, ignore_conflicts=False)
                elif num_active_reviews > 5:
                    pass

            review_source.sync_status = ReviewSource.SyncStatus.SYNCED
            print(f"{datetime.datetime.now()} - {total_reviews} : {avg_rating}")
            review_source.reviews_count = total_reviews
            print(f"{datetime.datetime.now()} - set total reviews")
            review_source.average_rating = round(avg_rating/2, 2)
            print(f"{datetime.datetime.now()} - set avg rating")
            review_source.save()
            print(f"done")
            # new flow
            # active_review = Review.objects.filter(is_active=True)
            # for row in active_review:
            #     Reviews = Review.objects.filter(source_id=review_source.id, author=row.author, date=row.date,
            #                                     is_active=False)
            #     try:
            #         external_id = Reviews.values_list('external_id', flat=True)[0]
            #         row.update(external_id=external_id)
            #     except:
            #         pass
            #     Reviews.delete()

        except SoftTimeLimitExceeded as e:
            review_source.sync_status = ReviewSource.SyncStatus.FALSE
            review_source.save()
            print(e)

        # return reviews
