# coding=utf8
from __future__ import unicode_literals, absolute_import, print_function
from collections import Counter

from django.db import models
from django.db import transaction
from django.db.models import Count, Q
from django.utils import timezone
from gm_types.gaia import SERVICE_COMMENT_OPTION as SCO
from gm_types.gaia import SERVICE_COMMENT_OPTION_TYPES as SCOT
from gm_upload import IMG_TYPE, ImgUrlField

from api.models import Order, Service, ServiceItemKey
from api.tool.datetime_tool import get_timestamp_or_none
from api.tool.image_utils import get_full_path
from api.tool.user_tool import filter_user_nick_name, get_doctor_by_user_id, get_portrait_by_user_id

# 定义展示文案 评价列表页面展示的评论tags
COMMENT_OPTION_SHOW_MAP = {
    # 服务态度
    SCO.ATTITUDE1: u'态度差',
    SCO.ATTITUDE2: u'态度待改进',
    SCO.ATTITUDE3: u'态度一般',
    SCO.ATTITUDE4: u'态度良好',
    SCO.ATTITUDE5: u'态度棒',
    # 专业技能
    SCO.PROFESSIONAL1: u'不专业',
    SCO.PROFESSIONAL2: u'专业待提升',
    SCO.PROFESSIONAL3: u'专业一般',
    SCO.PROFESSIONAL4: u'专业良好',
    SCO.PROFESSIONAL5: u'专业技能高',
    # 就医环境
    SCO.ENVIRONMENT1: u'环境差',
    SCO.ENVIRONMENT2: u'环境需改进',
    SCO.ENVIRONMENT3: u'环境一般',
    SCO.ENVIRONMENT4: u'环境良好',
    SCO.ENVIRONMENT5: u'环境棒',
    # 卫生条件
    SCO.SANITATION1: u'卫生差',
    SCO.SANITATION2: u'卫生待改进',
    SCO.SANITATION3: u'卫生一般',
    SCO.SANITATION4: u'卫生良好',
    SCO.SANITATION5: u'干净卫生',
    # 私密性
    SCO.PRIVATE1: u'私密性差',
    SCO.PRIVATE2: u'私密性待改进',
    SCO.PRIVATE3: u'私密性一般',
    SCO.PRIVATE4: u'私密性良好',
    SCO.PRIVATE5: u'私密性好',
    # 私信回复
    SCO.MESSAGE1: u'私信不回',
    SCO.MESSAGE2: u'私信回复慢',
    SCO.MESSAGE3: u'私信回复一般',
    SCO.MESSAGE4: u'私信回复较快',
    SCO.MESSAGE5: u'私信回复快',

    SCO.SALES5: u'没有推销',
    SCO.WITHIMAGE: u'有图',
    SCO.MEDIUM: u'中评',
    SCO.POSITIVE: u'好评',
    SCO.NEGATIVE: u'差评',
}

class ServiceComment(models.Model):
    POSITIVE_RATING_MIN = 4
    NEGATIVE_RATING_MAX = 2
    MEDIUM_RATING = 3

    service = models.ForeignKey(Service, verbose_name=u'关联美购')
    order = models.ForeignKey(Order, verbose_name=u'关联订单', unique=True)
    rating = models.IntegerField(verbose_name=u'总评分')
    operation_effect = models.IntegerField(verbose_name=u'术后效果评分')
    doctor_attitude = models.IntegerField(verbose_name=u'医生态度')
    hospital_env = models.IntegerField(verbose_name=u'就医环境')
    content = models.TextField(verbose_name=u'评价内容', max_length=640, null=True, blank=True)
    create_time = models.DateTimeField(default=timezone.now)
    is_online = models.BooleanField(verbose_name=u"是否上线", default=True)
    with_images = models.BooleanField(verbose_name=u'是否包含图片', default=False)
    default_rating = models.BooleanField(verbose_name=u'是否默认好评', default=False)

    # 冗余字段 用来存放service_item_id
    service_item_id = models.IntegerField(verbose_name=u'sku_id', null=True, default=None, db_index=True)
    user_id = models.IntegerField(verbose_name=u'user_id', null=True, default=None, db_index=True)

    is_effect = models.BooleanField(verbose_name=u'是否有效', default=True)

    @classmethod
    def create_comment(cls, service, order, rating, images, operation_effect, doctor_attitude,
                       hospital_env, content, comment_options, default_rating=False):
        doctor = get_doctor_by_user_id(order.user_id)
        if doctor:
            is_effect = False
        else:
            is_effect = True

        if (default_rating or content == '') and order.service_item_id:
            num = ServiceComment.objects.filter(service_item_id=order.service_item_id,
                                                user_id=order.user_id).count()
            if num > 0:
                is_effect = False

        with transaction.atomic():
            obj_kwargs = {
                'service': service,
                'order': order,
                'rating': rating,
                'operation_effect': operation_effect,
                'doctor_attitude': doctor_attitude,
                'hospital_env': hospital_env,
                'content': content,
                'default_rating': default_rating,
                'user_id': order.user_id,
                'service_item_id': order.service_item_id,
                'is_effect': is_effect
            }
            comment = cls.objects.create(**obj_kwargs)

            if comment_options:
                for option in comment_options:
                    _ = ServiceCommentOption.create_option(comment, option)

            if images:
                for image_url in images:
                    _ = ServiceCommentImage.save_image(comment, image_url)
                comment.with_images = True

            comment.save()

            return comment

    def images_info(self):
        images = self.image.all()
        if not images:
            return []

        return [image.image_data() for image in images]

    def serviceitem_name(self):
        show_name = ''
        items_name_list = self.order.order_items_name_list()

        if items_name_list:
            show_name = ' '.join(items_name_list)

        if show_name == '':
            show_name = self.service.name

        return show_name

    def user_info(self):
        user = self.order.user

        return {
            'user_id': user.id,
            'last_name': filter_user_nick_name(user),
            'user_portrait': user.userextra.get_portrait(),
        }

    def comment_info_for_list(self):
        content = self.content
        content_original_none = False
        if self.default_rating:
            content = u'该用户默认好评啦~'
        if not content:
            content = u'此用户没有填写评价。'
            content_original_none = True
        data = {
            'id': self.id,
            'rating': self.rating,
            'create_time': get_timestamp_or_none(self.create_time),
            'content': content,
            'images': self.images_info(),
            'service_name': self.serviceitem_name(),
            'user_info': self.user_info(),

            'default_rating': self.default_rating,
            'content_original_none': content_original_none,
            'order_id': self.order.id,
            'operation_effect': '{:.1f}'.format(self.operation_effect or 0),
            'doctor_attitude': '{:.1f}'.format(self.doctor_attitude or 0),
            'hospital_env': '{:.1f}'.format(self.hospital_env or 0),
        }

        return data

    def comment_detail(self):
        data = {
            'portrait': get_portrait_by_user_id(self.order.user_id),
            'rating': self.rating,
            'content': self.content,
            "operation_effect_rating": self.operation_effect,
            "doctor_attitude_rating": self.doctor_attitude,
            "hospital_env_rating": self.hospital_env,
            "images": []
        }
        comment_option = []
        options_list = ServiceCommentOption.all_level_options[self.rating - 1]['options']
        options = self.servicecommentoption_set.all().values_list('option_content', flat=True)
        for option in options_list:
            if option['option_id'] in options:
                comment_option.append({
                    'option_name': option['option_name'],
                    'is_selected': option['option_id'] in options
                })
        data['comment_option'] = comment_option
        for image in self.image.all():
            data['images'].append(get_full_path(image.image_data(), '-thumb'))
        return data


class ServiceCommentImage(models.Model):
    service_comment = models.ForeignKey(ServiceComment, related_name='image')
    image = ImgUrlField(u'图片URL', img_type=IMG_TYPE.SERVICE_COMMENT, max_length=64, blank=True)

    @classmethod
    def save_image(cls, service_comment, image_url):
        obj = cls.objects.create(service_comment=service_comment, image=image_url)
        return obj

    def image_data(self):
        return self.image


class ServiceCommentOption(models.Model):
    negative_options = (
        SCO.ATTITUDE1, SCO.ATTITUDE2,
        SCO.PROFESSIONAL1, SCO.PROFESSIONAL2,
        SCO.ENVIRONMENT1, SCO.ENVIRONMENT2,
        SCO.SANITATION1, SCO.SANITATION2,
        SCO.PRIVATE1, SCO.PRIVATE2,
        SCO.MESSAGE1, SCO.MESSAGE2,
        SCO.NEGATIVE
    )
    medium_options = (
        SCO.ATTITUDE3, SCO.PROFESSIONAL3, SCO.ENVIRONMENT3, SCO.SANITATION3,
        SCO.PRIVATE3, SCO.MESSAGE3, SCO.MEDIUM,
    )
    positive_options = (
        SCO.ATTITUDE4, SCO.ATTITUDE5, SCO.PROFESSIONAL4, SCO.PROFESSIONAL5,
        SCO.ENVIRONMENT4, SCO.ENVIRONMENT5, SCO.SANITATION4, SCO.SANITATION5,
        SCO.PRIVATE4, SCO.PRIVATE5, SCO.MESSAGE4, SCO.MESSAGE5, SCO.SALES5,
        SCO.POSITIVE, SCO.WITHIMAGE,
    )
    all_level_options = [
        {
            'rating': 1,
            'options': [
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.ATTITUDE1,
                    'option_name': SCO.getDesc(SCO.ATTITUDE1),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.PROFESSIONAL1,
                    'option_name': SCO.getDesc(SCO.PROFESSIONAL1),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.ENVIRONMENT1,
                    'option_name': SCO.getDesc(SCO.ENVIRONMENT1),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.SANITATION1,
                    'option_name': SCO.getDesc(SCO.SANITATION1),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.PRIVATE1,
                    'option_name': SCO.getDesc(SCO.PRIVATE1),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.MESSAGE1,
                    'option_name': SCO.getDesc(SCO.MESSAGE1),
                },
            ],
        },
        {
            'rating': 2,
            'options': [
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.ATTITUDE2,
                    'option_name': SCO.getDesc(SCO.ATTITUDE2),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.PROFESSIONAL2,
                    'option_name': SCO.getDesc(SCO.PROFESSIONAL2),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.ENVIRONMENT2,
                    'option_name': SCO.getDesc(SCO.ENVIRONMENT2),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.SANITATION2,
                    'option_name': SCO.getDesc(SCO.SANITATION2),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.PRIVATE2,
                    'option_name': SCO.getDesc(SCO.PRIVATE2),
                },
                {
                    'option_type': SCOT.NEGATIVE,
                    'option_id': SCO.MESSAGE2,
                    'option_name': SCO.getDesc(SCO.MESSAGE2),
                },
            ],
        },
        {
            'rating': 3,
            'options': [
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.ATTITUDE3,
                    'option_name': SCO.getDesc(SCO.ATTITUDE3),
                },
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.PROFESSIONAL3,
                    'option_name': SCO.getDesc(SCO.PROFESSIONAL3),
                },
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.ENVIRONMENT3,
                    'option_name': SCO.getDesc(SCO.ENVIRONMENT3),
                },
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.SANITATION3,
                    'option_name': SCO.getDesc(SCO.SANITATION3),
                },
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.PRIVATE3,
                    'option_name': SCO.getDesc(SCO.PRIVATE3),
                },
                {
                    'option_type': SCOT.MEDIUM,
                    'option_id': SCO.MESSAGE3,
                    'option_name': SCO.getDesc(SCO.MESSAGE3),
                },
            ],
        },
        {
            'rating': 4,
            'options': [
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.ATTITUDE4,
                    'option_name': SCO.getDesc(SCO.ATTITUDE4),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.PROFESSIONAL4,
                    'option_name': SCO.getDesc(SCO.PROFESSIONAL4),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.ENVIRONMENT4,
                    'option_name': SCO.getDesc(SCO.ENVIRONMENT4),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.SANITATION4,
                    'option_name': SCO.getDesc(SCO.SANITATION4),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.PRIVATE4,
                    'option_name': SCO.getDesc(SCO.PRIVATE4),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.MESSAGE4,
                    'option_name': SCO.getDesc(SCO.MESSAGE4),
                },
            ],
        },
        {
            'rating': 5,
            'options': [
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.ATTITUDE5,
                    'option_name': SCO.getDesc(SCO.ATTITUDE5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.PROFESSIONAL5,
                    'option_name': SCO.getDesc(SCO.PROFESSIONAL5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.ENVIRONMENT5,
                    'option_name': SCO.getDesc(SCO.ENVIRONMENT5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.SANITATION5,
                    'option_name': SCO.getDesc(SCO.SANITATION5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.PRIVATE5,
                    'option_name': SCO.getDesc(SCO.PRIVATE5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.MESSAGE5,
                    'option_name': SCO.getDesc(SCO.MESSAGE5),
                },
                {
                    'option_type': SCOT.POSITIVE,
                    'option_id': SCO.SALES5,
                    'option_name': SCO.getDesc(SCO.SALES5),
                },
            ],
        },
    ]
    service_comment = models.ForeignKey(ServiceComment)
    option_content = models.CharField(choices=SCO, max_length=3)

    @classmethod
    def create_option(cls, comment, option):
        _option = None
        if option in SCO:
            _option = cls.objects.create(service_comment=comment, option_content=option)

        return _option


class ServiceCommentManager(object):
    def get_comment_list(self, service_id, comment_option=None, start_num=0,
                         count=10, order_ids=None, rate=None):
        show_comments = ServiceComment.objects.filter(service_id=service_id,
                                                      is_online=True, is_effect=True)
        if start_num > show_comments.count():
            return []

        if comment_option in [SCO.NEGATIVE, SCO.POSITIVE, SCO.WITHIMAGE, SCO.MEDIUM]:
            if comment_option == SCO.POSITIVE:
                show_comments = show_comments.filter(rating__gte=ServiceComment.POSITIVE_RATING_MIN)
            elif comment_option == SCO.NEGATIVE:
                show_comments = show_comments.filter(rating__lte=ServiceComment.NEGATIVE_RATING_MAX)
            elif comment_option == SCO.WITHIMAGE:
                show_comments = show_comments.filter(with_images=True)
            elif comment_option == SCO.MEDIUM:
                show_comments = show_comments.filter(rating=ServiceComment.MEDIUM_RATING)

        elif comment_option in SCO:
            comment_options = ServiceCommentOption.objects.filter(service_comment__in=show_comments,
                                                                  option_content=comment_option)
            show_comment_ids = comment_options.values_list('service_comment').distinct().values_list('service_comment',
                                                                                                     flat=True)
            show_comments = ServiceComment.objects.filter(id__in=show_comment_ids)

        if order_ids:  # list
            show_comments = show_comments.filter(order__id__in=order_ids)

        if rate:
            show_comments = show_comments.filter(rating__gte=rate)

        default_comment = list(show_comments.filter(default_rating=True))
        nondefault_comment = show_comments.filter(default_rating=False)
        comments_with_content = list(
            nondefault_comment.filter(Q(content__isnull=False) & ~Q(content='')).order_by('-rating', '-create_time'))
        comments_no_content = list(
            nondefault_comment.filter(Q(content__isnull=True) | Q(content='')).order_by('-rating', '-create_time'))

        total_comment = comments_with_content + comments_no_content + default_comment

        total_comment_count = len(total_comment)
        if start_num + count > total_comment_count:
            show_comments = total_comment[start_num:]
        else:
            show_comments = total_comment[start_num:start_num + count]

        result = []
        for comment in show_comments:
            result.append(comment.comment_info_for_list())

        return result

    def get_comment_count(self, service_id, need_default_praise=False):
        comments = ServiceComment.objects.filter(service_id=service_id, is_online=True, is_effect=True)
        result = {
            "all_count": comments.count(),
            "default_rating_count": 0,
        }

        if need_default_praise:
            result["default_rating_count"] = comments.filter(default_rating=True).count()

        return result

    def get_comment_option_content_show_for_aggregate(self, option):
        '''
        将数据库存储的评论tag转换为评论聚合页面,展示的精简版文案,如果未配置简版, 则使用原始文案
        :params: option 数据库中存储的标签简写, a1/a2/p1/p2等
        :return 返回对应标签
            eg:     
                option-a1    # a1表示 '服务态度差'
                return: '态度差'
        '''
        return COMMENT_OPTION_SHOW_MAP.get(option) or SCO.getDesc(option)

    def get_comment_detail(self, service_id, option_count_min=0):
        all_comments = ServiceComment.objects.filter(service_id=service_id, is_online=True, is_effect=True)
        # positive_comment_count = all_comments.filter(rating__gte=ServiceComment.POSITIVE_RATING_MIN).count()
        # negative_comment_count = all_comments.filter(rating__lte=ServiceComment.NEGATIVE_RATING_MAX).count()
        # medium_comment_count = all_comments.filter(rating=ServiceComment.MEDIUM_RATING).count()
        service_comment_ids = list(all_comments.values_list('id', flat=True))

        show_options = ServiceCommentOption.objects.filter(Q(service_comment__in=service_comment_ids))\
            .values_list('option_content', flat=True)

        primary_options_collections = ServiceCommentOption.positive_options + ServiceCommentOption.medium_options
        # remove the positive, negative and medium option
        specific_primary_options = [option for option in primary_options_collections
                                    if option not in (SCO.POSITIVE, SCO.MEDIUM)]
        specific_negative_options = [option for option in ServiceCommentOption.negative_options
                                     if option != SCO.NEGATIVE]
        primary_options = [option for option in show_options if option in specific_primary_options]
        negative_options = [option for option in show_options if option in specific_negative_options]
        cpo = Counter(primary_options)
        cno = Counter(negative_options)
        primary_options = [{'option_content': po, 'count': cpo[po]} for po in cpo if cpo[po] >= option_count_min]
        primary_options.sort(key=lambda x: x['count'], reverse=True)
        negative_options = [{'option_content': no, 'count': cno[no]} for no in cno if cno[no] >= option_count_min]
        negative_options.sort(key=lambda x: x['count'])
        show_options = primary_options + negative_options

        # if positive_comment_count > 0:
        #     option_data.append(
        #         {
        #             'count': positive_comment_count,
        #             'id': SCO.POSITIVE,
        #             'type': SCOT.POSITIVE,
        #             'name': SCO.getDesc(SCO.POSITIVE),
        #         }
        #     )
        #
        # if negative_comment_count > 0:
        #     option_data.append(
        #         {
        #             'count': negative_comment_count,
        #             'id': SCO.NEGATIVE,
        #             'type': SCOT.NEGATIVE,
        #             'name': SCO.getDesc(SCO.NEGATIVE),
        #         }
        #     )
        #
        # if medium_comment_count > 0:
        #     option_data.append(
        #         {
        #             'count': medium_comment_count,
        #             'id': SCO.MEDIUM,
        #             'type': SCOT.MEDIUM,
        #             'name': SCO.getDesc(SCO.MEDIUM),
        #         }
        #     )
        comments_with_images_count = all_comments.filter(with_images=True).count()
        option_data = []

        if comments_with_images_count > 0:
            option_data.append(
                {
                    'count': comments_with_images_count,
                    'id': SCO.WITHIMAGE,
                    'type': SCOT.POSITIVE,
                    'name': self.get_comment_option_content_show_for_aggregate(SCO.WITHIMAGE)
                }
            )

        for option in show_options:
            _data = {
                'count': option['count'],
                'id': option['option_content'],
                'type': (
                    SCOT.POSITIVE
                    if option['option_content'] in ServiceCommentOption.positive_options
                    else SCOT.NEGATIVE
                ),
                'name': self.get_comment_option_content_show_for_aggregate(option['option_content']),
            }
            option_data.append(_data)

        result = {'comment_options': option_data}
        return result

    def get_comment_detail_with_comment(self, service_id, option_count_min=10):
        if not option_count_min:
            option_count_min = 10
        result = self.get_comment_detail(service_id=service_id, option_count_min=option_count_min)
        all_comments = ServiceComment.objects.filter(service_id=service_id, is_online=True, is_effect=True)
        result['all_comments_count'] = all_comments.count()

        positive_comments = all_comments.filter(rating__gte=ServiceComment.POSITIVE_RATING_MIN)
        top_rating_comment = positive_comments.filter(Q(content__isnull=False) & ~Q(content='') & Q(default_rating=False)).order_by(
            '-rating', '-create_time').first()

        if top_rating_comment:
            result['show_comment'] = [top_rating_comment.comment_info_for_list()]
        else:
            result['show_comment'] = []

        return result


service_comment_manager = ServiceCommentManager()
