#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import logging
import random
import requests
import time

from django.db import transaction
from django.views import View

from Controller.DetectController import DetectControllerView
from Controller.DeviceConfirmRegion import Device_Region
from Model.models import SerialNumberModel, CompanySerialModel, UIDCompanySerialModel, CompanyModel, RegionModel, \
    CountryModel, UIDModel, Device_Info, iotdeviceInfoModel, UidPushModel, LogModel, MacModel, UidSetModel, UID_Bucket, \
    Unused_Uid_Meal, Order_Model, StsCrdModel, VodHlsModel, ExperienceContextModel, Equipment_Info, UidUserModel
from Object.RedisObject import RedisObject
from Object.uidManageResponseObject import uidManageResponseObject
from Object.TokenObject import TokenObject
from Service.AlgorithmService import AlgorithmBaseOn35
from Service.CommonService import CommonService
from Ansjer.config import CRCKey, SERVER_TYPE, SERVER_DOMAIN_US, SERVER_DOMAIN_CN
from Service.ModelService import ModelService
from Object.AWS.S3Email import S3Email


class SerialNumberView(View):

    def get(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation', None)
        request_dict = request.GET
        return self.validate(request_dict, operation, request)

    def post(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation', None)
        request_dict = request.POST
        return self.validate(request_dict, operation, request)

    def validate(self, request_dict, operation, request):
        token = TokenObject(request_dict.get('token', None))
        response = uidManageResponseObject()

        if operation == 'getUID':
            return self.do_get_uid(request_dict, response)
        elif operation == 'attachUID':      # 绑定uid
            return self.do_attach_uid(request_dict, response, request)
        elif operation == 'detachUID':      # 解绑uid
            return self.do_detach_uid(request, request_dict, response)
        elif operation == 'create':
            return self.do_create(request_dict, response)
        elif operation == 'changeSerialNumberStatus':
            return self.changeSerialNumberStatus(request_dict, response)
        else:
            if token.code != 0:
                return response.json(token.code)

            # if operation == 'create':
            #     return self.do_create(request_dict, response)
            elif operation == 'list':
                return self.do_list(token.userID, request_dict, response)
            elif operation == 'update':
                return self.do_update(token.userID, request_dict, response)
            else:
                return response.json(404)

    def do_create(self, request_dict, response):
        quantity = int(request_dict.get('quantity', 0))

        if not quantity:
            return response.json(444)

        try:
            try:
                sum = SerialNumberModel.objects.last().id
            except:
                sum = 0
            serial_number_bulk = []
            now_time = int(time.time())
            algorithm = AlgorithmBaseOn35()
            for i in range(quantity):
                serial_number = algorithm.getLetter(sum)
                sum += 1  # sum每次递增1
                # 前面补0至六位
                serial_number = (6 - len(serial_number)) * '0' + serial_number
                serial_number_bulk.append(SerialNumberModel(serial_number=serial_number, add_time=now_time))
            # 开启事务写入
            with transaction.atomic():
                SerialNumberModel.objects.bulk_create(serial_number_bulk)
            return response.json(0)
        except Exception as e:
            print(e)
            return response.json(500, repr(e))

    def do_list(self, userID, request_dict, response):
        # perm = ModelService.check_perm_uid_manage(userID, 0)
        # if not perm:
        #     return response.json(309)

        page = request_dict.get('page', None)
        line = request_dict.get('limit', None)
        serial_number = request_dict.get('serial_number', None)
        status = request_dict.get('status', None)

        if page and line:
            page = int(page)
            line = int(line)

            serial_qs = SerialNumberModel.objects.filter().order_by('-id')

            if serial_number:
                serial_qs = serial_qs.filter(serial_number__contains=serial_number)

            if status:
                serial_qs = serial_qs.filter(status=status)

            count = serial_qs.count()

            start = (page - 1) * line
            end = start + line

            serial_qs = serial_qs[start: end].values()
            res = {
                'count': count,
                'data': list(serial_qs)
            }

            return response.json(0, res)
        else:
            return response.json(444)

    def do_attach_uid(self, request_dict, response, request):
        serial_number = request_dict.get('serial_number', None)
        country_id = request_dict.get('country_id', None)
        company_id = request_dict.get('company_id', None)
        token = request_dict.get('token', None)
        time_stamp = request_dict.get('time_stamp', None)
        p2p_type = request_dict.get('p2ptype', 1)

        if not all([serial_number, company_id, token, time_stamp]):
            return response.json(444)

        # 时间戳token校验
        if not CommonService.check_time_stamp_token(token, time_stamp):
            return response.json(13)

        now_time = int(time.time())
        serial = serial_number[0:6]
        full_serial = serial_number[0:9]

        if serial_number[9:10]:
            p2p_type = serial_number[9:10]

        try:
            if not country_id:
                # 设备模拟国外环境测试
                # if SERVER_TYPE == 'Ansjer.us_config.formal_settings':   # 国外正式配置使用固定ip进行测试
                #     ip = '67.220.90.13'
                # else:
                #     ip = CommonService.get_ip_address(request)

                ip = CommonService.get_ip_address(request)
                country_id = Device_Region().get_device_region(ip)

            # 判断序列号是否已和企业关联
            company_serial_qs = CompanySerialModel.objects.filter(company__secret=company_id, serial_number=serial)
            if not company_serial_qs.exists():
                return response.json(173)

            # 当序列号已关联UID
            company_serial = company_serial_qs[0]

            if company_serial.status == 0:  # 该序列号未绑定企业
                return response.json(173)
            elif company_serial.status == 1:    # 绑定uid
                # redis加锁，防止同一个序列号重复绑定
                key = serial + 'do_attach_uid'
                redisObj = RedisObject()
                isLock = redisObj.CONN.setnx(key, 1)
                redisObj.CONN.expire(key, 60)
                if not isLock:
                    return response.json(5)
                with transaction.atomic():
                    count = 0
                    while count < 3:
                        # 查询是否存在未绑定序列号的uid
                        uid_qs = UIDModel.objects.filter(vpg__company_id=company_serial.company.id,
                                                         vpg__region_id=country_id, status=0, p2p_type=p2p_type). \
                                                         order_by('id')
                        if not uid_qs.exists():
                            return response.json(375)

                        uid = uid_qs[0]
                        # 判断uid是否已绑定过序列号
                        uid_company_serial_qs = UIDCompanySerialModel.objects.filter(uid_id=uid.id)
                        if uid_company_serial_qs.exists():
                            return response.json(377)

                        result = UIDModel.objects.filter(id=uid.id, status=0).update(status=2, update_time=now_time)
                        if int(result) <= 0:    # 更新失败
                            count += 1
                            continue

                        # UID关联【企业关联序列号】表创建数据
                        UIDCompanySerialModel.objects.create(uid_id=uid.id, company_serial_id=company_serial.id,
                                                             add_time=now_time, update_time=now_time)

                        company_serial.status = 2
                        company_serial.save()

                        dev = Device_Info.objects.filter(UID=uid.uid)
                        if dev.exists():
                            dev.update(serial_number=full_serial)

                        full_uid_code = uid.full_uid_code
                        if uid.platform in CRCKey.keys():
                            full_uid_code += ':'+CRCKey[uid.platform]

                        res = {
                            'full_uid_code': CommonService.encode_data(full_uid_code),
                            'uid': CommonService.encode_data(uid.uid),
                            'extra': uid.uid_extra,
                            'platform': uid.platform,
                            'initString': uid.init_string,
                            'initStringApp': uid.init_string_app,
                        }

                        # 记录操作日志
                        ip = CommonService.get_ip_address(request)
                        content = json.loads(json.dumps(request_dict))
                        log = {
                            'ip': ip,
                            'user_id': 1,
                            'status': 200,
                            'time': now_time,
                            'content': json.dumps(content),
                            'url': 'serialNumber/attachUID',
                            'operation': '序列号{}绑定uid: {}'.format(serial, uid.uid),
                        }
                        LogModel.objects.create(**log)
                        # 修改其他数据库的序列号使用状态为已占用
                        update_success = self.update_serial_status(serial=serial, status=3)
                        try:
                            assert update_success
                        except AssertionError:
                            return response.json(378)
                        redisObj.del_data(key=key)
                        return response.json(0, res)
                    return response.json(5)
            elif company_serial.status == 2:   # 返回uid
                uid_qs = UIDCompanySerialModel.objects.filter(company_serial_id=company_serial.id)
                if not uid_qs.exists():
                    return response.json(173)
                uid = uid_qs.values('uid__uid', 'uid__mac', 'uid__uid_extra', 'uid__full_uid_code',
                                    'uid__platform', 'uid__init_string', 'uid__init_string_app')[0]
                full_uid_code = uid['uid__full_uid_code']
                if uid['uid__platform'] in CRCKey.keys():
                    full_uid_code += ':'+CRCKey[uid['uid__platform']]
                res = {
                    'full_uid_code': CommonService.encode_data(full_uid_code),
                    'uid': CommonService.encode_data(uid['uid__uid']),
                    'extra': uid['uid__uid_extra'],
                    'platform': uid['uid__platform'],
                    'initString': uid['uid__init_string'],
                    'initStringApp': uid['uid__init_string_app'],
                }
                return response.json(0, res)
            elif company_serial.status == 3:
                return response.json(10042)
        except Exception as e:
            djangoLogger = logging.getLogger('django')
            djangoLogger.exception(repr(e))
            return response.json(176, str(e))

    def do_get_uid(self, request_dict, response):
        serial_number = request_dict.get('serial_number', None)
        token = request_dict.get('token', None)
        time_stamp = request_dict.get('time_stamp', None)

        if token and time_stamp and serial_number:
            # 时间戳token校验
            if not CommonService.check_time_stamp_token(token, time_stamp):
                return response.json(13)

            mark = serial_number[6:9]
            serial = serial_number[0:6]
            uid_company_serial_qs = UIDCompanySerialModel.objects.filter(company_serial__company__mark=mark,
                                                                         company_serial__serial_number__serial_number=serial)

            if uid_company_serial_qs.exists():
                uid = uid_company_serial_qs.values('uid__uid', 'uid__mac', 'uid__uid_extra')[0]

                res = {
                    'uid': CommonService.encode_data(uid['uid__uid']),
                    'mac': CommonService.encode_data(uid['uid__mac']),
                    'extra': uid['uid__uid_extra']
                }
                return response.json(0, res)
            else:
                return response.json(173)
        else:
            return response.json(444)

    def do_detach_uid(self, request, request_dict, response):
        token = request_dict.get('token', None)
        time_stamp = request_dict.get('time_stamp', None)
        serial_number = request_dict.get('serial_number', None)

        if not all([token, time_stamp, serial_number]):
            return response.json(444)

        # 时间戳token校验
        if not CommonService.check_time_stamp_token(token, time_stamp):
            return response.json(13)

        now_time = int(time.time())
        serial = serial_number[0:6]
        company_serial_qs = CompanySerialModel.objects.filter(serial_number=serial).values('status')
        if not company_serial_qs.exists():
            return response.json(379)
        status = company_serial_qs[0]['status']
        if status == 1:
            return response.json(0, {'success': '序列号未绑定uid'})
        elif status == 3:
            return response.json(10043)
        uid_serial_qs = UIDCompanySerialModel.objects.filter(company_serial__serial_number=serial)
        if not uid_serial_qs.exists():
            return response.json(173)
        uid_serial = uid_serial_qs[0]

        try:
            with transaction.atomic():
                uid = uid_serial.uid.uid
                company_serial_qs.update(status=1)      # 更新序列号状态
                # 删除设备相关数据,参考后台的设备重置删除的数据
                Device_Info.objects.filter(UID=uid).delete()
                UidSetModel.objects.filter(uid=uid).delete()
                UidUserModel.objects.filter(UID=uid).delete()
                Equipment_Info.objects.filter(devUid=uid).delete()
                iotdeviceInfoModel.objects.filter(serial_number=serial).delete()

                # 重置设备云存
                UID_Bucket.objects.filter(uid=uid).delete()
                Unused_Uid_Meal.objects.filter(uid=uid).delete()
                Order_Model.objects.filter(UID=uid).delete()
                StsCrdModel.objects.filter(uid=uid).delete()
                VodHlsModel.objects.filter(uid=uid).delete()
                ExperienceContextModel.objects.filter(uid=uid).delete()

                # 修改其他数据库的序列号使用状态为已使用
                update_success = self.update_serial_status(serial=serial, status=1)
                try:
                    assert update_success
                except AssertionError:
                    return response.json(378)

                UIDModel.objects.filter(uid=uid).update(status=0, mac='')    # 重置uid的使用状态为未分配
                uid_serial.delete()

                # 记录操作日志
                ip = CommonService.get_ip_address(request)
                content = json.loads(json.dumps(request_dict))
                log = {
                    'ip': ip,
                    'user_id': 1,
                    'status': 200,
                    'time': now_time,
                    'content': json.dumps(content),
                    'url': 'serialNumber/detachUID',
                    'operation': '序列号{}解绑uid: {}'.format(serial, uid),
                }
                LogModel.objects.create(**log)
            return response.json(0)
        except Exception as e:
            djangoLogger = logging.getLogger('django')
            djangoLogger.exception(repr(e))
            return response.json(176, str(e))

    @staticmethod
    def update_serial_status(serial, status):
        if SERVER_TYPE == 'Ansjer.cn_config.test_settings':   # 测试服不处理
            return True
        if SERVER_TYPE == 'Ansjer.us_config.formal_settings':
            server_domain = SERVER_DOMAIN_CN
        elif SERVER_TYPE == 'Ansjer.cn_config.formal_settings':
            server_domain = SERVER_DOMAIN_US
        url = server_domain + 'serialNumber/changeSerialNumberStatus'
        data = {'serial': serial, 'status': status}
        req_res = requests.post(url=url, data=data, timeout=2)
        if req_res.status_code != 200:
            return False
        req_res = req_res.json()
        if req_res['result_code'] != 0:
            return False
        return True

    def do_update(self, userID, request_dict, response):
        # perm = ModelService.check_perm_uid_manage(userID, 0)
        # if not perm:
        #     return response.json(309)

        id = request_dict.get('id', None)
        status = request_dict.get('status', None)

        if id and status:
            serial_number_qs = SerialNumberModel.objects.filter(id=id)
            if serial_number_qs.exists():
                serial_number_qs.update(**{'status': status})
                return response.json(0)
            else:
                return response.json(173)
        else:
            return response.json(444)

    # 序列号绑定和解绑uid时修改其他数据库序列号的使用状态
    def changeSerialNumberStatus(self, request_dict, response):
        serial = request_dict.get('serial', None)
        status = request_dict.get('status', None)
        if not all([serial, status]):
            return response(444)

        try:
            # 更新CompanySerialModel表的序列号使用状态
            update = CompanySerialModel.objects.filter(serial_number=serial).update(status=int(status))
            if update:
                return response.json(0)
            else:
                return response.json(378)
        except Exception as e:
            djangoLogger = logging.getLogger('django')
            djangoLogger.exception(repr(e))
            return response.json(500, str(e))

