# coding=utf8

from __future__ import unicode_literals, absolute_import, print_function

from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.html import escape

from gm_types.gaia import TOPIC_TYPE
from gm_types.gaia import PRIVATE_STATUS
from gm_types.gaia import PROBLEM_FLAG_CHOICES
from gm_types.gaia import PROBLEM_STATUS_CHOICES
from gm_types.gaia import PROBLEM_REVIEW_STATUS_CHOICES
from gm_types.gaia import AUTHOR_TYPE
from gm_types.gaia import SLIDE_TYPE

from gm_upload import ImgUrlField
from gm_upload import IMG_TYPE

from talos.services.doctor import DoctorService
from talos.services.hospital import HospitalService
from talos.services.user import UserService
from talos.services.goods import GoodsService
from talos.services.tag import TagService
from talos.services import other

from talos.libs.image_utils import get_full_path
from talos.libs.datetime_utils import get_timestamp
from talos.libs.datetime_utils import get_timestamp_or_none

from .activity import Activity
from .pgcclassify import PgcClassify
from ..diary.diary import Diary

# TODO
from api.tool import const_strings
from rpc.cache import ViewRecord


class ShareTopicManager(models.Manager):
    def get_queryset(self):
        return super(ShareTopicManager, self).get_queryset().filter(topic_type=TOPIC_TYPE.SHARE,
                                                                    private_status=PRIVATE_STATUS.NORMAL,
                                                                    is_online=True, flag='n')


class Problem(models.Model):
    """topic.

    NOTE:
        update model ActivityWinlist's topic definition if pk replace with non-default field(`id`)
    """

    class Meta:
        verbose_name = u'01. 帖子'
        verbose_name_plural = u'01. 帖子'
        db_table = 'api_problem'
        permissions = (
            ("can_answer", "Can answer user questions."),
        )
        app_label = 'talos'

    def __unicode__(self):
        id1 = u"%d : " % self.id
        if self.ask:
            return id1 + escape(self.ask[:40])
        else:
            return id1

    objects = models.Manager()

    share_topics = ShareTopicManager()

    user_id = models.IntegerField(default=None, null=True, blank=True, verbose_name=u"提问用户外键id")
    order = models.IntegerField(default=20, verbose_name=u"展示顺序", help_text=u"小的排在前，大的排在后")
    title = models.CharField(max_length=200, db_index=True, null=True, blank=True, default=None, verbose_name=u"标题")
    ask = models.TextField(default='', blank=True, verbose_name=u'话题标题')
    answer = models.TextField(default='', blank=True, verbose_name=u'话题内容')
    diary = models.ForeignKey(Diary, null=True, related_name=u'topics')

    created_time = models.DateTimeField(db_index=True, verbose_name=u"问题创建时间", default=timezone.now)
    last_modified = models.DateTimeField(db_index=True, verbose_name=u"最后修改时间", default=timezone.now, null=True)

    # 用户的状态(用户删除)
    flag = models.CharField(max_length=1, default=PROBLEM_FLAG_CHOICES.NORMAL,
                            choices=PROBLEM_FLAG_CHOICES, db_index=True)
    status = models.CharField(max_length=1, default=PROBLEM_STATUS_CHOICES.NEW,
                              choices=PROBLEM_STATUS_CHOICES, db_index=True)
    review_status = models.IntegerField(choices=PROBLEM_REVIEW_STATUS_CHOICES,
                                        default=PROBLEM_REVIEW_STATUS_CHOICES.NOT_REVIEW)

    # 是哪个医生回答的(两者修改最好同步)
    # 指定给哪个医生回答，私信的医生，也是这个
    assigned_doctor_id = models.IntegerField(default=None, null=True, blank=True, verbose_name=u'指定的医生外键id')
    doctor_id = models.CharField(max_length=100, default=None, null=True, blank=True)

    has_answer = models.BooleanField(default=False, db_index=True, verbose_name=u"有回答")

    is_online = models.BooleanField(default=True, help_text=u"是否可以上线", verbose_name=u"上线", db_index=True)
    is_push = models.BooleanField(u'所长发帖页面是否为推荐的专栏', default=False, db_index=True)
    can_video_cover = models.BooleanField(default=True, help_text=u"是否使用此日记贴的短视频作为封面", verbose_name=u"使用此日记贴的短视频作为封面")

    # 该问题是否公开
    is_public = models.BooleanField(default=True, help_text=u"是否公开", verbose_name=u"公开", db_index=True)
    is_sink = models.BooleanField(default=False, help_text=u"是否下沉", verbose_name=u"下沉")
    is_headline = models.BooleanField(default=False, verbose_name=u"首页推荐", db_index=True)
    channel_headline = models.BooleanField(default=False, verbose_name=u"频道推荐", db_index=True)
    is_spam = models.BooleanField(verbose_name=u'是否为疑似广告', default=False, db_index=True)

    apply_phone = models.CharField(default='', max_length=20, verbose_name=u'活动报名手机号')

    # 是否私信问题
    private_status = models.CharField(max_length=1,
                                      choices=PRIVATE_STATUS,
                                      default=PRIVATE_STATUS.NORMAL,
                                      verbose_name=u"隐私状态",
                                      db_index=True, null=True)

    # this field is reused as elite(essence) flag
    selected_qa = models.BooleanField(default=False, help_text=u"优选问答", verbose_name=u"优选问答")

    is_recommend = models.BooleanField(verbose_name=u"频道置顶", default=False, db_index=True)
    is_recommend_all = models.BooleanField(verbose_name=u"全局置顶", default=False, db_index=True)

    # 如果为话题，则所有的回答都在 TopicReply中
    # 如果是问答，则将原有的问答转换成为话题呢? 首先: assigned_doctor必须有效
    is_topic = models.BooleanField(default=True, verbose_name=u"是否为话题")

    # 参考: api.management.commands.problem_meta_update
    doctor_num = models.IntegerField(default=0, verbose_name=u"有多少医生回复")
    reply_num = models.IntegerField(default=0, verbose_name=u"有多少回复")
    author_reply_num = models.IntegerField(default=0, verbose_name=u'楼主回复的数量')

    # 话题类型
    topic_type = models.CharField(max_length=10, null=False, verbose_name=u'话题类型',
                                  default=TOPIC_TYPE.ASK, choices=TOPIC_TYPE, db_index=True)
    service_id = models.IntegerField(null=True, blank=True, verbose_name=u'关联美购外键id')

    # 案例相关字段 Since 3.7
    link_doctor_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'关联医生外键id')
    raw_doctor = models.CharField(verbose_name=u'医生（名称）', blank=True, default='', max_length=200)
    link_hospital_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'关联医院外键id')
    raw_hospital = models.CharField(verbose_name=u'医院（名称）', blank=True, default='', max_length=200)

    rating = models.IntegerField(verbose_name=u'评价', blank=True, default=0)
    price = models.IntegerField(verbose_name=u'价格', blank=True, default=0)

    operation_time = models.DateField(verbose_name=u'手术日期', blank=True, null=True, default=None)

    # v 7.6.25 新加字段 用于记录用户自己选择的术后日期
    operation_date = models.DateTimeField(db_index=True, verbose_name=u"用户选择的术后日期", default=timezone.now)

    pre_operation_image = ImgUrlField(img_type=IMG_TYPE.TOPIC, verbose_name=u'术前图', max_length=128, default='',
                                      blank=True, null=True)
    post_operation_image = ImgUrlField(img_type=IMG_TYPE.TOPIC, verbose_name=u'术后图', max_length=128, default='',
                                       blank=True, null=True)

    activity = models.ForeignKey(Activity, null=True, default=None)  # added @5.5
    device_id = models.CharField(verbose_name=u'发帖时的设备号', max_length=64, blank=True, default=None,
                                 null=True)  # added at 5.7
    is_mixed = models.BooleanField(default=False, help_text=u"是否是图文混排", verbose_name=u"图文混排")

    # 分享文案相关
    share_id = models.IntegerField(default=None, null=True, verbose_name=u'分享文案外键id')

    # pgc相关
    pgc_classfy = models.ForeignKey(PgcClassify, null=True, blank=True, related_name='topics',
                                    verbose_name=u'分类')
    audit_status = models.BooleanField(default=True, help_text=u"审核状态", verbose_name=u"审核状态")
    # 所长发帖添加富文本
    answer_richtext = models.TextField(default='', blank=True, verbose_name=u'话题内容')

    link_doctor_item_id = models.IntegerField(default=None, null=True)

    """
    FIELDS DEPRECATED

    mysql fields:

      `activity_title` varchar(100) DEFAULT NULL,
      `body_part_id` int(11) DEFAULT NULL,
      `bodypart_subitem_id` int(11) DEFAULT NULL,
      `collect_num` int(11) NOT NULL DEFAULT '0',
      `other_item` varchar(100) DEFAULT '',
      `has_activity` tinyint(1) NOT NULL,
      `jump_type` varchar(1) NOT NULL,
      `jump_value` varchar(100) DEFAULT NULL,
      `link_doctor_item_id` int(11) DEFAULT NULL,
      `is_viewed` tinyint(1) NOT NULL DEFAULT '1',
      `is_alerted` tinyint(1) NOT NULL,
      `patient_image` varchar(255) DEFAULT NULL,

    """
    has_activity = models.BooleanField(verbose_name=u'是否有活动链接', default=False)
    jump_type = models.CharField(max_length=1, null=False, choices=SLIDE_TYPE, default=SLIDE_TYPE.CREATE_TOPIC)
    is_alerted = models.BooleanField(help_text=u"是否已经提醒", verbose_name=u"已经提醒", default=False)
    apply_phone = models.CharField(default='', max_length=20, verbose_name=u'活动报名手机号')
    # FIELDS DEPRECATED END


    def get_tags(self):
        """problems's tags."""
        ts = []
        for tag in self.tags:
            data = {
                'name': tag.name,
                'tag_id': tag.id,
                'tag_type': tag.tag_type,
            }
            ts.append(data)
        return ts

    @staticmethod
    def _get_vote_amount_of_id(topic_id):
        return ViewRecord(const_strings.TOPIC_VOTE)[topic_id] or 0      # TODO CR ViewRecord & const_strings

    @property
    def vote_amount(self):
        return self._get_vote_amount_of_id(self.id)

    @staticmethod
    def get_view_amount_of_id(topic_id):
        return ViewRecord(const_strings.TOPIC_VIEW)[topic_id] or 0      # TODO CR ViewRecord & const_strings

    @property
    def view_amount(self):
        return self.get_view_amount_of_id(self.id)

    @property
    def author_type(self):
        return AUTHOR_TYPE.USER

    @property
    def get_title_style_type(self):
        return ''

    def get_title(self):
        t = self.title or self.ask or ''
        return escape(t)

    @property
    def content(self):
        if self.topic_type == TOPIC_TYPE.COLUMN_ARTICLE:
            return self.ask

        if self.user_id == settings.SUOZHANG_UID or self.topic_type == TOPIC_TYPE.WEIXIN_NUM:
            _content = self.answer
        else:
            _content = escape(self.answer)

        return _content

    def get_topic_info_from_db(self, user=None):
        me = UserService.get_user_by_user_id(self.user_id)

        portrait = me.portrait
        city_name = me.city_name or u'中国'
        membership_level = me.membership_level

        if self.diary and self.diary.operation_time:
            operation_time = get_timestamp(self.diary.operation_time)
        else:
            operation_time = None

        data = {
            'user': {
                'user_id': self.user_id,
                'user_name': me.nickname,
                'portrait': portrait,
                'city': city_name,
                'membership_level': membership_level,
                'vote_num_gained': me.vote_count,
                'topic_num_posted': me.topic_count,
                'user_level': {
                    'level_icon': me.level_icon,
                    'membership_icon': me.membership_icon,
                    'constellation_icon': me.constellation_icon,
                },
            },
            'problem': {
                'topic_id': self.id,
                'topic_type_id': self.topic_type,
                'is_topic': self.is_topic,
                'title': self.get_title(),
                'content': self.content,
                'ask': self.ask,
                'images': [],
                'images_raw': [],  # dict of images info
                'pre_operation_images': [],

                'is_private': False,
                'last_modified_time': get_timestamp_or_none(self.last_modified),
                'created_time': get_timestamp_or_none(self.created_time),
                'doctor_num': self.doctor_num,
                'reply_num': self.reply_num,
                'is_voted': False,
                'vote_num': 0,
                'view_num': 0,
                'title_style_type': self.get_title_style_type,
                'operation_time': operation_time,
                'tags': self.get_tags(),
                'is_favord': False,

                'interval': '',
                'activity_id': self.activity and self.activity.id or None,
                'activity_title': self.activity and self.activity.title or None,
                'is_mixed': self.is_mixed,
                'from_weixin': True if self.topic_type == TOPIC_TYPE.WEIXIN_NUM else False,
                'user_name': me.nickname,
                'rich_text': self.answer_richtext,
                'author_type': self.author_type,
                'operation_record': self.operation_record,

                # NOTE: deprecated
                'is_recommend': self.is_recommend,
                'is_recommend_all': self.is_recommend_all,
                'doctor_id': '',
                'service_name': None,
                'patient_image': '',
                'patient_image_thumb': '',
                'zone_list': [],
                # NOTE: deprecated end
            },
            'replies': [],  # for compitable

            'share_data': self.get_topic_share_data_from_db(),
        }

        if data['problem']['from_weixin']:    # 产品的锅
            data['title_replace_content'] = True
        else:
            data['title_replace_content'] = False

        data['problem']['pre_id'] = ''
        data['problem']['next_id'] = ''
        try:
            data['problem']['favord_count'] = self.favor_problem.filter(is_deleted=False).count()
        except:
            # 无收藏数据
            data['problem']['favord_count'] = 0

        if self.diary:
            data['operation_info'] = self.diary.get_operation_info_for_topic_detail()

            data['diary'] = {
                'diary_id': self.diary.id,
                'topic_num': self.diary.topics.filter(flag=PROBLEM_FLAG_CHOICES.NORMAL, is_online=True).count(),
                'operator_is_hospital_officer': self.diary.operator_is_hospital_officer,
                'title': self.diary.title,
                'tags': self.diary.tags_new_era,

                # NOTE: checked topic/detail list page, seems like diary's vote num is not used
                'vote_num': 0,
            }

            # 返回当前帖子是日记本中第几篇帖子
            # v 7.6.25 修改排序规则，之前按照 created_time 现按照新加字段 operation_date 排序
            topics = self.diary.topics.filter(is_online=True).order_by('-operation_date')
            if topics:
                topics_id = list(topics.values_list('id', flat=True))
                try:
                    cur_index = topics_id.index(self.id)
                    data['problem']['diary_num'] = len(topics_id) - cur_index
                    data['problem']['pre_id'] = topics_id[cur_index - 1] if not cur_index == 0 else ''
                    data['problem']['next_id'] = topics_id[cur_index + 1] if not (cur_index+1) == len(topics_id) else ''
                except:
                    pass  # NOTE: 帖子下线 不需要处理
            else:
                data['problem']['diary_num'] = 1

        else:
            data['diary'] = None
            data['operation_info'] = None

        if self.pre_operation_image:
            data['problem']['pre_operation_images'].append(get_full_path(self.pre_operation_image))
        else:
            for image in self.pre_operation_images.order_by('id'):
                data['problem']['pre_operation_images'].append(get_full_path(image.image_url))

        if self.post_operation_image:
            data['problem']['images'].append(get_full_path(self.post_operation_image))

        for image in self.images.order_by('id'):
            img_url = get_full_path(image.image_url)
            data['problem']['images'].append(img_url)
            data['problem']['images_raw'].append({
                'image_url': img_url,
                'taken_time': get_timestamp_or_none(image.taken_time),
            })

        if self.topic_type == TOPIC_TYPE.SHARE and self.diary and self.diary.operation_time:
            interval = self.created_time - self.diary.operation_time
            if interval.days >= 0:
                data['problem']['interval'] = u'{}'.format(interval.days + 1)
            else:
                data['problem']['interval'] = u'以前'

        # 当只有一张图的时候，显示大图
        data['problem']['show_large_images'] = len(data['problem']['images']) == 1
        data['problem'].update(self.get_video_info())

        data['problem']['replay'] = self.get_live_replay_info()
        data['problem']['replay_app'] = self.get_live_replay_info_to_app(user=user)
        return data

    def get_live_replay_info(self):
        try:
            return self.stream.get_topic_reply_info()
        except:
            return {
                'source': '',
                'cover': '',
                'content': ''
            }

    def get_live_replay_info_to_app(self, user):
        try:
            return self.stream.replay_data(user)
        except:
            return {}

    def get_topic_share_data_from_db(self):
        if self.share:
            return {
                'share_title': self.share.title,
                'share_content': self.share.content,
                'share_moments': self.share.moments,
                'share_weibo': self.share.weibo,
                'share_image': self.share.image
            }
        else:
            return None

    def is_voted(self, user_id):
        is_voted = self.votes.filter(user_id=user_id).exists() if user_id else False
        return is_voted

    def is_author_followed_by(self, user_id):
        if user_id:
            from social import SocialInfo        # TODO CR social 相关
            social_info = SocialInfo(uid=user_id)
            return social_info.is_following_user(self.user_id)

        return False

    def is_favored_by(self, user_id):
        return self.favor_problem.filter(
            user_id=user_id, is_deleted=False
        ).exists()

    def get_topic_info_of_user(self, user):
        return {
            'is_following': self.is_author_followed_by(user.id) if user else False,
            'is_voted': self.is_voted(user.id) if user else False,
            'is_favord': bool(user) and self.is_favored_by(user.id) or False,
            'is_private': self.user_id == user.id if user else False,
        }

    @classmethod
    def get_topic_info_from_redis(cls, topic_id):
        return {
            'vote_num': cls._get_vote_amount_of_id(topic_id),
            'view_num': cls.get_view_amount_of_id(topic_id),
        }

    @property
    def view_num(self):
        return self.get_view_amount_of_id(self.id)

    @staticmethod
    def get_topic_info_combined(from_db, of_user, from_redis):
        from_db['problem'].update(of_user)
        from_db['problem'].update(from_redis)
        return from_db

    def get_topic_info(self, user=None):
        return self.get_topic_info_combined(
            from_db=self.get_topic_info_from_db(user=user),
            of_user=self.get_topic_info_of_user(user=user),
            from_redis=self.get_topic_info_from_redis(self.id),
        )

    def get_video_info(self):
        try:
            return self.video.get_video_info()
        except:
            return {
                'video_url': '',
                'video_pic': ''
            }

    def has_video_url(self):
        try:
            return self.video.video_url
        except:
            return False

    def data(self):
        _data = {}
        _data["topic"] = topic = {}
        topic["content"] = escape(self.answer)
        images_data = []
        for image in self.images.order_by('id'):
            img_url = get_full_path(image.image_url)
            image_data = {}
            image_data['image'] = img_url
            image_data["image_name"] = image.image_url
            image_data["image_type"] = image.image_type
            images_data.append(image_data)
        topic["images"] = images_data
        return _data

    def set_review_status(self, image_count):
        if (len(self.answer) < settings.CASHBACK_TOPIC_CONTENT_WORDS_LIMIT or
           image_count < settings.CASHBACK_TOPIC_IMAGE_LIMIT):
            self.review_status = PROBLEM_REVIEW_STATUS_CHOICES.CONTENT_LIMIT
        else:
            self.review_status = PROBLEM_REVIEW_STATUS_CHOICES.NOT_REVIEW

    def update_reply_num(self, is_online=False, reply_num=0):
        if not self.diary:
            return False
        if not reply_num:
            reply_num = self.reply_num

        if is_online:
            self.diary.reply_num += reply_num
        else:
            self.diary.reply_num -= reply_num

        if self.diary.reply_num < 0:
            return False

        self.diary.save()
        return True

    @property
    def visible(self):
        return self.is_online and self.flag == PROBLEM_FLAG_CHOICES.NORMAL

    @classmethod
    def get_by_id(cls, id):
        try:
            t = cls.objects.get(id=id)
            return t
        except cls.DoesNotExist:
            return None

    @property
    def user(self):
        return UserService.get_user_by_user_id(self.user_id)

    @property
    def service(self):
        return GoodsService.get_service_by_service_id(self.service_id)

    @property
    def share(self):
        return other.get_share_by_share_id(self.share_id)

    @property
    def assigned_doctor(self):
        # TODO: error ?
        return UserService.get_user_by_user_id(self.assigned_doctor_id)

    @property
    def doctor(self):
        return DoctorService.get_doctor_from_doctor_id(self.doctor_id)

    @property
    def operations(self):
        from .problem_operations import ProblemOperations   # 避免循环引用
        os = ProblemOperations.objects.filter(problem=self)
        return os

    @cached_property
    def tag_ids(self):
        from .problemtag import ProblemTag      # 避免循环引用
        tag_ids = ProblemTag.objects.filter(problem=self).values_list('tag_id', flat=True)
        return list(tag_ids)

    @cached_property
    def tags(self):
        _tags = TagService.get_tags_by_tag_ids(self.tag_ids)
        return _tags or []

    def add_tags(self, tag_ids):
        from .problemtag import ProblemTag      # 避免循环引用
        for tag_id in tag_ids:
            ProblemTag.objects.get_or_create(problem_id=self.id, tag_id=tag_id)

    def upd_tags(self, tag_ids):
        new_tags = set([int(tag_id) for tag_id in tag_ids if str(tag_id).isdigit()])
        old_tags = set(self.tag_ids)
        self.add_tags(list(new_tags - old_tags))
        self.del_tags(list(old_tags - new_tags))

    def del_tags(self, tag_ids):
        from .problemtag import ProblemTag      # 避免循环引用
        ProblemTag.objects.filter(problem_id=self.id, tag_id__in=tag_ids).delete()

    def clear_tags(self):
        from .problemtag import ProblemTag      # 避免循环引用
        ProblemTag.objects.filter(problem_id=self.id).delete()

    @property
    def link_doctor(self):
        return DoctorService.get_doctor_from_doctor_id(self.link_doctor_id)

    @property
    def link_hospital(self):
        return HospitalService.get_hospital_from_hospital_id(self.link_hospital_id)

    @property
    def operation_record(self):
        if not (self.diary and self.diary.operation_time):
            return ''

        timedel = self.created_time - self.diary.operation_time
        if timedel.days == 0:
            return u"#手术当日#"
        elif timedel.days < 0:
            return u""
        else:
            return u'#术后第{}天＃'.format(timedel.days)
