#!/usr/bin/env python
# coding=utf-8
from django.conf import settings

from gm_types.gaia import DOCTOR_TYPE
from gm_types.gaia import DOCTOR_IDENTITY

from rpc.cache import doctor_switch_cache
from rpc.tool.error_code import CODES, gen
from rpc.tool.log_tool import doctor_switch_logger

from doctor.models.person import DoctorStaffLogin

from api.models import UserRelatedDoctorgroup
from hippo.models import Doctor


def vaildate_staff_person_id(person_id):
    '''
    判断person_id是否是合法运营
    '''
    is_validate = False
    if person_id:
        try:
            DoctorStaffLogin.objects.get(person_id=person_id, can_login=True)
            is_validate = True
        except DoctorStaffLogin.DoesNotExist:
            pass
    return is_validate


class DoctorSwitch(object):

    origin_id = None  # staff_id or officer_id
    origin_indentity = None  # 管理者staff或者officer的身份
    doctor_indentity = None  # self.doctor indentity

    def __init__(self, ctx, doctor):
        if not doctor:
            return gen(CODES.DOCTOR_NOT_FOUND)
        self.ctx = ctx
        self.session = ctx.session.session_key
        self.doctor = doctor
        self._init_origin_id()

    def can_switch(self):
        """
            是否可以切换医生
            * 若是从officer/staff切换过来时
                当officer/staff身份已取消，应使session.do_logout()
                当前医生账号不在原管理者列表中时，应使session.do_logout()
                由于session_info有100s的缓存，所以当切换账号时，出现上述两个情况直接登出
        """
        can_switch = False
        if self.origin_id:  # 医生是管理者 或者 是从管理者切换过来的时候
            can_switch = True

        if self.doctor_indentity == DOCTOR_IDENTITY.DOCTOR:
            # 是从别的账号切换过来的, 若自己不在管理列表中了是需要登出的
            can_switch = self.judge_target_doctor(self.doctor.id, need_logout=True)

            if self.origin_indentity == DOCTOR_IDENTITY.STAFF and not self.doctor.self_run:
                # 运营只能管理 自营医生(self_run=True)
                # 若是运营切换过来的自营医生, 变成了非自营 需要登出
                can_switch = False
                self.ctx.session.do_logout()

        return can_switch

    def do_switch(self, target_doctor_id):
        """
            切换账号
            规则：
                * 若是staff/officer, 根据staff_id/officer_id 直接切换
                * 若不是staff且不是officer, 从redis根据session取，判断该session是否是通过staff/officer 切换过来的,
                    同时获取staff_id/officer_id 进行切换
                * 切换之后，把target_doctor_session和staff或者officer对应存到redis中,用于之后获取账号切换来源
            正常情况返回session
        """
        start_log = u'current doctor_id is %s, session is %s, want switch doctor_id is %s' % (
            self.doctor.id, self.session, target_doctor_id,
        )
        doctor_switch_logger.info(start_log)

        if not self.can_switch():
            return gen(CODES.DOCTOR_SWITCH_NO_PERMISSION)

        login_doctor = Doctor.objects.get(id=target_doctor_id)
        # 若是自营管理的，自营医生变成了非自营，不让切换(可以切换回自己的运营身份)
        if self.origin_indentity == DOCTOR_IDENTITY.STAFF and self.origin_id != str(login_doctor.user.person.id) \
                and not login_doctor.self_run:
            return gen(CODES.DOCTOR_SWITCH_NOT_SELFRUN)

        if not self.judge_target_doctor(target_doctor_id):
            # 主要判断目标医生 是否在 切换列表中, self.can_switch 中judge_target_doctor是判断自己的
            return gen(CODES.DOCTOR_SWITCH_NOTALLOW_TARGET)

        user = login_doctor.user
        if not user:
            return gen(CODES.DOCTOR_NOT_USER)

        login_session = self.get_doctor_session(login_doctor)
        self._cache_doctor(login_session)

        end_log = u'from doctor_id is %s, switch to doctor_id is %s, origin %s id is %s' % (
            self.doctor.id, target_doctor_id,
            self.origin_indentity, self.origin_id,
        )
        doctor_switch_logger.info(end_log)
        return login_session

    def get_doctor_session(self, login_doctor):
        return self.ctx.rpc_remote['passport/login/direct_create_session'](
            username=login_doctor.user._get_full_field_content_('username'),
            purpose='ascle-om-1',
            proofs={
                'organization_manager': {
                    'user_id': self.doctor.user.id,
                    'doctor_id': self.doctor.id,
                    'hospital_id': 'hospital_id',
                },
                'target_doctor': {
                    'user_id': login_doctor.user.id,
                    'doctor_id': login_doctor.id,
                    'hospital_id': 'hospital_id',
                },
                "constraints": {
                    "for_organization_manager_to_access_ascle_website_as_doctors_which_is_of_the_hospital": True,
                    "this_session_is_stored_in_ascle_server_and_wont_be_exposed_to_user": True
                }
            }
        ).unwrap()['session_id']

    def judge_target_doctor(self, target_doctor_id, need_logout=False):
        """
            判断目标医生是否能够切换(目标医生是否在自己的可切换列表中)
        """
        allow = False
        if self.origin_indentity == DOCTOR_IDENTITY.OFFICER:  # 从officer切换
            officer = Doctor.objects.filter(id=self.origin_id, doctor_type=DOCTOR_TYPE.OFFICER).first()
            if officer:
                # 切换至从连锁商户
                try:
                    merchant = officer.d_merchant
                    t_merchant = Doctor.objects.get(id=target_doctor_id).merchant
                    from hippo.models.chain_hospital import MasterMerchant, SlaveMerchant
                    mastermerchant = MasterMerchant.objects.filter(
                        mastermerchant_id=merchant, is_login_related=True).first()
                    if mastermerchant:
                        if SlaveMerchant.objects.filter(
                                mastermerchant_id=mastermerchant.id, slavemerchant_id=t_merchant.id).exists():
                            allow = True
                except:
                    pass
                # 判断前医生是否还在管理名单内
                officer_allow = Doctor.objects.filter(
                    id=target_doctor_id,
                    hospital=officer.hospital,
                    allow_hospital_agent=True).exists()
                if officer_allow or (self.origin_id == target_doctor_id):
                    allow = True
        elif self.origin_indentity == DOCTOR_IDENTITY.STAFF:  # 从staff切换
            if vaildate_staff_person_id(self.origin_id):
                # 查找 运营对应的医生
                staff_doctor = Doctor.objects.filter(user__person__id=self.origin_id).first()
                if self.doctor == staff_doctor or self.doctor in self.get_self_run_doctors():
                    # 判断自己是否还在自营医生列表中
                    allow = True

        if not allow and need_logout:
            self.ctx.session.do_logout()

        return allow

    def get_self_run_doctors(self):
        """
            获取 自营医生列表
        """
        doctors = []
        for urdg in UserRelatedDoctorgroup.objects.filter(person_id=self.origin_id):  # 获取运营管理的组
            for d in urdg.doctorgroup.doctors():
                if d not in doctors:
                    doctors.append(d)
        return doctors

    def get_switch_doctors(self):
        """
            获取切换医生列表
        """
        doctors = []
        if self.origin_indentity == DOCTOR_IDENTITY.OFFICER:
            try:
                doctor = Doctor.objects.get(id=self.origin_id)
                from hippo.models.chain_hospital import MasterMerchant
                mastermerchants = MasterMerchant.objects.filter(
                    mastermerchant_id=doctor.d_merchant.id, is_login_related=True)
            except:
                mastermerchants = None
            try:
                officer = Doctor.objects.get(id=self.origin_id, doctor_type=DOCTOR_TYPE.OFFICER)
                for doctor in Doctor.objects.filter(hospital=officer.hospital, is_show=True, allow_hospital_agent=True):
                    doctors.append(doctor)
                if officer not in doctors:
                    doctors.insert(0, officer)
            except Doctor.DoesNotExist:
                pass
            if mastermerchants and mastermerchants.exists():
                merchant_ids = mastermerchants.first().slavemerchants.\
                    values_list('slavemerchant_id', flat=True)
                s_doctors = Doctor.objects.filter(d_merchant__id__in=merchant_ids)
                for s_doctor in s_doctors:
                    if s_doctor.doctor_type == DOCTOR_TYPE.OFFICER:
                        for d in Doctor.objects.filter(hospital=s_doctor.hospital, is_show=True, allow_hospital_agent=True):
                            doctors.append(d)
                    if s_doctor not in doctors:
                        doctors.append(s_doctor)
        elif self.origin_indentity == DOCTOR_IDENTITY.STAFF:
            if vaildate_staff_person_id(self.origin_id):
                staff_doctor = Doctor.objects.filter(is_show=True, user__person__id=self.origin_id).first()
                doctors = self.get_self_run_doctors()
                if staff_doctor not in doctors:
                    doctors.insert(0, staff_doctor)
        return doctors

    def _cache_doctor(self, target_session):
        """
            把目标医生的session和origin_id进行关联
                * staff 缓存 person_id
                * officer 缓存 doctor_id
        """
        key = self._gen_cache_key(self.origin_indentity, target_session)
        doctor_switch_cache.set(key, self.origin_id, settings.ASCLE_USER_COOKIE_TIME)

    def _gen_cache_key(self, indentity, session):
        return u'{}:{}'.format(indentity, session)

    def _init_origin_id(self):
        """
            * 判断医生是否是officer, origin_id 使用doctor_id
            * 判断医生是否是staff, origin_id 使用person_id
            * 若是普通医生，根据session去redis获取他的管理者
            * 从redis中获取管理者，先找officer，然后再找staff
        """
        officer_id = doctor_switch_cache.get(self._gen_cache_key(DOCTOR_IDENTITY.OFFICER, self.session))
        staff_id = doctor_switch_cache.get(self._gen_cache_key(DOCTOR_IDENTITY.STAFF, self.session))
        if officer_id:
            self.origin_id = officer_id
            self.doctor_indentity = DOCTOR_IDENTITY.DOCTOR  # 当做是 普通医生，因为是从别的账号切换过来的
            self.origin_indentity = DOCTOR_IDENTITY.OFFICER
        elif staff_id:
            self.origin_id = staff_id
            self.doctor_indentity = DOCTOR_IDENTITY.DOCTOR  # 当做是 普通医生，因为是从别的账号切换过来的
            self.origin_indentity = DOCTOR_IDENTITY.STAFF
        elif self.doctor.doctor_type == DOCTOR_TYPE.OFFICER:  # 判断是否是机构管理者登录
            self.origin_id = self.doctor.id
            self.origin_indentity = DOCTOR_IDENTITY.OFFICER
            self.doctor_indentity = DOCTOR_IDENTITY.OFFICER
        elif vaildate_staff_person_id(self.doctor.user.person.id):  # 判断是否是运营
            self.origin_id = str(self.doctor.user.person.id)
            self.origin_indentity = DOCTOR_IDENTITY.STAFF
            self.doctor_indentity = DOCTOR_IDENTITY.STAFF
