# coding=utf-8
from __future__ import print_function, unicode_literals, absolute_import


from django.core.management.base import BaseCommand, CommandError
from talos.models.topic import Problem
from talos.models.diary import Diary
from talos.views.topic.data_sync import get_topics, get_topics_gql
from talos.views.diary.data_sync import get_diaries
from trans2es.utils.diary_transfer import get_diary
from trans2es.utils.topic_transfer import get_problem
from api.models.diary import Diary as DiaryOld
from api.models.topic import Problem as ProblemOld
import random
import copy
import json


class Command(BaseCommand):
    RAND_CNT = 200  # may get RAND_CNT or RAND_CNT+1 pks

    @classmethod
    def dict_diff(cls, left, right, ignore_paths=None):
        ignore_paths = ignore_paths or []
        diff_path = []

        def type_diff(left, right):
            if type(left) is type(right):
                return False
            if isinstance(left, (int, long)) and isinstance(right, (int, long)):
                return False
            if isinstance(left, dict) and isinstance(right, dict):
                return False
            return True

        def diff(left, right, path='$', show_diff=True):
            if path in ignore_paths:
                return True
            if type_diff(left, right):
                if show_diff:
                    diff_path.append(path)
                return False

            if type(left) is dict:  # dict
                lkeys = left.keys()
                rkeys = right.keys()
                for k in lkeys:
                    new_path = path+'.'+k
                    if new_path not in ignore_paths and k not in right:
                        if show_diff:
                            diff_path.append(new_path)
                        return False
                for k in rkeys:
                    new_path = path+'.'+k
                    if new_path not in ignore_paths and k not in left:
                        if show_diff:
                            diff_path.append(new_path)
                        return False

                for k, v in left.items():
                    new_path = path+'.'+k
                    if new_path not in ignore_paths and not diff(v, right[k], new_path, show_diff):
                        if show_diff:
                            diff_path.append(new_path)
                        return False

                return True

            elif type(left) is list:  # list
                if len(left) != len(right):
                    if show_diff:
                        diff_path.append(path)
                    return False

                # find the same one by one:
                left_copy = copy.copy(left)
                right_copy = copy.copy(right)
                while len(left_copy) > 0:
                    x = left_copy.pop()
                    for i, y in enumerate(right_copy):
                        if diff(x, y, path, False):
                            right_copy.pop(i)
                            break
                    else:
                        if show_diff:
                            diff_path.append(path)
                        return False

                return True
            else:  # otherwise
                if left != right:
                    if show_diff:
                        diff_path.append(path)
                    return False
                else:
                    return True

        if not diff(left, right):
            print('diff path:', diff_path[0])
            return False
        else:
            return True

    def add_arguments(self, parser):
        parser.add_argument('--topic-id', dest='topic_ids', metavar='TOPIC_ID', type=int, nargs='*')
        parser.add_argument('--diary-id', dest='diary_ids', metavar='DIARY_ID', type=int, nargs='*')
        parser.add_argument('--diff-type', dest='diff_type', metavar='DIFF_TYPE', type=int, nargs=1)

    def rand_pks(self, model):
        total_cnt = model.objects.count()
        step = total_cnt / self.RAND_CNT
        base = random.randint(0, step-1)
        res = []
        for i in range(base, total_cnt, step):
            x = model.objects.order_by('id')[i:i+1]
            res.append(x[0].id)
        return res

    def handle(self, *args, **kwargs):
        if kwargs['diff_type']==1:
            self.diff1(*args, **kwargs)
        else:
            self.diff2(*args, **kwargs)

    def diff1(self, *args, **kwargs):
        if not kwargs['topic_ids']:
            topic_ids = self.rand_pks(Problem)
        else:
            topic_ids = kwargs['topic_ids']
        topic_ignore_path = ['$.ranking_popularity', '$.suggest', '$.is_diary_sink_or_topic_sink', '$.user.last_name',]

        if not kwargs['diary_ids']:
            diary_ids = self.rand_pks(Diary)
        else:
            diary_ids = kwargs['diary_ids']
        diary_ignore_path = ['$.user.last_name',]

        topics = ProblemOld.objects.filter(id__in=topic_ids)
        topics_new = get_topics.func(topic_ids)
        for topic_old in topics:
            topic_new = topics_new[topic_old.id]
            _topic_old = get_problem(topic_old)
            if not self.dict_diff(_topic_old, topic_new, topic_ignore_path):
                print('TOPIC LEFT:%s' % str(_topic_old))
                print('TOPIC RIGHT:%s' % str(topic_new))
                break

        diaries = DiaryOld.objects.filter(id__in=diary_ids)
        diaries_new = get_diaries.func(diary_ids)
        for diary_old in diaries:
            diary_new = diaries_new[diary_old.id]
            _diary_old = get_diary(diary_old)
            if not self.dict_diff(_diary_old, diary_new, diary_ignore_path):
                print('DIARY LEFT:%s' % str(_diary_old))
                print('DIARY RIGHT:%s' % str(diary_new))
                break

    def diff2(self, *args, **kwargs):
        if not kwargs['topic_ids']:
            topic_ids = self.rand_pks(Problem)
        else:
            topic_ids = kwargs['topic_ids']
        topic_ignore_path = ['$.ranking_popularity', '$.suggest', '$.is_diary_sink_or_topic_sink', '$.user.last_name',]

        topics_old = get_topics.func(topic_ids)
        topics_old = {topic['id']:topic for topic in topics_old}
        topics_new = get_topics_gql(topic_ids)
        topics_new = {topic['id']:topic for topic in topics_new}
        for topic_old in topics_old.values():
            topic_new = topics_new[topic_old['id']]
            if not self.dict_diff(topic_old, topic_new, topic_ignore_path):
                print('TOPIC LEFT:%s' % str(topic_old))
                print('TOPIC RIGHT:%s' % str(topic_new))
                break
