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

import requests
from billiard.exceptions import SoftTimeLimitExceeded
from bs4 import BeautifulSoup
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__)
headers = {'content-type': 'application/json',
           'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
max_crawler = settings.MAX_CRAWLER_SIZE



class BBBCrawler:
    def get_info(self, url):
        soup = BeautifulSoup(requests.get(url, headers=headers).text, 'html.parser')
        try:
            avg_rating = float(
                soup.select_one(
                    "div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.MuiCard-root.css-1dcbtqe > div > div > span").text.strip().replace(
                    '/5', '').replace(
                    'stars',
                    ''))
            print('avg_rating',avg_rating)
        except Exception as E:
            print('avg_rating E',E)
            avg_rating = 1.0
        try:
            total_reviews = int(
                soup.select_one(
                    "div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.MuiCard-root.css-1dcbtqe > div > p").text.replace(
                    "Customer Reviews", "").replace("Average of", "").strip())
            print('total_review', total_reviews)
        except Exception as E:
            print('total_reviews E',E)
            total_reviews = 1
        id_company = url.split("/")[-2]
        bbb_id = id_company.split("-")[-2]
        bussiness_id = id_company.split("-")[-1]
        return total_reviews, avg_rating, bbb_id, bussiness_id

    def get_reviews(self, url: str):
        try:
            total_reviews, avg_rating, bbbID, bussiness_id = self.get_info(url + "/customer-reviews")
            print(f'{total_reviews}, {avg_rating}, {bbbID}, {bussiness_id}')
            parsed_reviews = []
            page = 1
            total_page = 1
            while len(parsed_reviews) < total_reviews or page <= total_page:
                print(page)
                try:
                    crawl_url = f'https://www.bbb.org/api/businessprofile/customerreviews?page={page}&pageSize=10&businessId={bussiness_id}&bbbId={bbbID}&sort=reviewDate%20desc,%20id%20desc'
                    logger.debug(crawl_url)
                    r = requests.get(crawl_url, headers=headers)
                    json_response = json.loads(r.text)
                    if page == 1:
                        total_page = json_response['totalPages']
                    logger.debug(json.dumps(json_response, indent=2))
                    reviews = json_response['items']
                    parsed_reviews += [
                        {
                            'id': review['id'],
                            'author': review['displayName'],
                            'date': datetime.date(year=int(review['date']['year']),
                                                  month=int(review['date']['month']),
                                                  day=int(review['date']['day'])),
                            'headline': '',
                            'body': review['extendedText'][0]['text'] if review['hasExtendedText'] else review['text'],
                            'rating': review['reviewStarRating'],
                        }
                        for review in reviews
                    ]
                    if len(parsed_reviews) >= max_crawler:
                        parsed_reviews = parsed_reviews[:max_crawler]
                        break
                    page += 1
                except Exception as e:
                    print(e)
                    page += 1
            return total_reviews, avg_rating, parsed_reviews
        except Exception as E:
            print(E)
            return None

    def sync_reviews(self, review_source: ReviewSource, is_add):
        try:
            total_reviews, avg_rating, raw_reviews = self.get_reviews(review_source.url)
        except Exception as e:
            review_source.sync_status = ReviewSource.SyncStatus.FALSE
            review_source.save()
            print(e)
        print(f'{total_reviews}: {avg_rating}; {len(raw_reviews)}')
        formated_reviews = []
        for r in raw_reviews:
            formated_reviews.append(
                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'],
                )

            )
        print(f'{len(raw_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(formated_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 = avg_rating
            print(f"{datetime.datetime.now()} - set avg rating")
            review_source.save()
            print(f"done")

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

        # return reviews
