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

import requests
from bulk_update.helper import bulk_update
from django.db import transaction
from django.db.models import Count
from django.views import View

from Model.models import SerialNumberModel, UserModel, UserSerialNumberModel, UserUIDModel, CompanySerialModel, \
    MacModel, LogModel, CompanyModel
from Object.RedisObject import RedisObject
from Object.ResponseObject import ResponseObject
from Object.TokenObject import TokenObject
from Service.AlgorithmService import AlgorithmBaseOn35
from Service.CommonService import CommonService


class SerialNumberView(View):

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

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

    def validate(self, request_dict, operation, request):
        response = ResponseObject()
        if operation == 'create':
            return self.do_create(request_dict, response)
        elif operation == 'getSerial':
            return self.do_get_serial_number(request_dict, request, response)

        token = request_dict.get('token', None)
        token = TokenObject(token)
        if token.code != 0:
            return response.json(token.code)

        if operation == 'quantity':  # 查询当前可用的UID的数量
            return self.do_quantity(request_dict, token.userID, response)
        elif operation == 'allot':
            return self.do_allot(request_dict, response)
        elif operation == 'createSerial':  # 生成序列号
            return self.create_serial(request_dict, response, request)
        elif operation == 'serialCompany':  # 将序列号分匹配到指定企业
            return self.do_serial_company(request_dict, response, request)
        elif operation == 'revise/state':  # 修改序列号状态
            return self.revise_state(request_dict, response, request)
        else:
            return response.json(309)

    def revise_state(self, request_dict, response, request):
        """
        修改序列号状态
        @param request_dict:请求参数
        @param response: 响应对象
        """
        use_status = request_dict.get('useStatus', None)  # 序列号表的状态
        status = request_dict.get('status', None)   # 关联企业序列号表的状态

        if not all([use_status, status]):
            return response.json(444)
        serial_number_qs = SerialNumberModel.objects.filter(use_status=use_status).values('serial_number')
        if not serial_number_qs.exists():
            return response.json(173)
        serial_list = [item[key] for item in serial_number_qs for key in item]
        try:
            country_serial_qs = CompanySerialModel.objects.filter(serial_number__in=serial_list).values('status')
            country_serial_qs.filter(status=status).update(status=1)
            return response.json(0)
        except Exception as e:
            print(e)
            return response.json(500)

    def do_serial_company(self, request_dict, response, request):
        """
        将序列号分匹配到指定企业
        :param request_dict: 请求参数
        :param response: 响应对象
        :param request: 请求
        :return:
        """
        id = request_dict.get('id', None)
        quantity = request_dict.get('quantity', None)

        if not all([id, quantity]):
            return response.json(444)

        company_qs = CompanyModel.objects.filter(id=id)
        if not company_qs.exists():
            return response.json(444)

        sum_Serial = SerialNumberModel.objects.filter().count()
        sum_Serial_company = CompanySerialModel.objects.filter().count()
        sum_bind = sum_Serial - sum_Serial_company  # 剩余可绑定的序列号
        if int(quantity) > int(sum_bind):
            return response.json(10041)

        try:
            company = company_qs[0]
            start_1 = sum_Serial_company
            end_1 = int(start_1) + int(quantity)
            serial_qs = SerialNumberModel.objects.filter()[start_1:end_1]
            if not serial_qs.exists():
                return response.json(173)
            company_serial_bulk = []
            now_time = int(time.time())
            for item in serial_qs:  # 更新状态为已分配但未使用
                company_serial_bulk.append(CompanySerialModel(
                    status=3,
                    add_time=now_time,
                    update_time=now_time,
                    company_id=company.id,
                    serial_number=item.serial_number,
                ))
                SerialNumberModel.objects.filter(serial_number=item.serial_number).update(
                    use_status=3,
                    add_time=now_time
                )

            # 记录操作日志
            ip = CommonService.get_ip_address(request)
            content = json.loads(json.dumps(request_dict))
            log = {
                'ip': ip,
                'user_id': 1,
                'status': 200,
                'time': now_time,
                'url': 'company/createSerial',
                'content': json.dumps(content),
                'operation': '{}生成{}个Company Serial{}: {}'.format(company.name, quantity, '成功', '同步更新成功'),
            }

            with transaction.atomic():
                CompanySerialModel.objects.bulk_create(company_serial_bulk)
                company.quantity = CompanySerialModel.objects.filter(company_id=id).count()
                company.save()
                Log = LogModel.objects.create(**log)

                # 同步更新业务服务器和uid管理系统的企业序列号表
                url1 = 'http://test.zositechc.cn/company/createSerial'
                url2 = 'https://www.zositechc.cn/company/createSerial'
                url3 = 'http://www.dvema.com/company/createSerial'
                url4 = 'http://www.zositeche.com/company/createSerial'
                requests_data = {'id': id, 'quantity': quantity}

                res1 = requests.post(url=url1, data=requests_data, timeout=2 * 60)
                if res1.status_code != 200:
                    fail_reason = '请求测试服务器生成Company Serial响应状态码异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)
                res1 = res1.json()
                if res1['result_code'] != 0:
                    fail_reason = '测试服务器生成Company Serial发生异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)

                res2 = requests.post(url=url2, data=requests_data, timeout=2 * 60)
                if res2.status_code != 200:
                    fail_reason = '请求国内服务器生成Company Serial响应状态码异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)
                res2 = res2.json()
                if res2['result_code'] != 0:
                    fail_reason = '国内服务器生成Company Serial发生异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)

                res3 = requests.post(url=url3, data=requests_data, timeout=2 * 60)
                if res3.status_code != 200:
                    fail_reason = '请求国外服务器生成Company Serial响应状态码异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)
                res3 = res3.json()
                if res3['result_code'] != 0:
                    fail_reason = '国外服务器生成Company Serial发生异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)

                res4 = requests.post(url=url4, data=requests_data, timeout=2 * 60)
                if res4.status_code != 200:
                    fail_reason = '请求uid管理系统生成Company Serial响应状态码异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)
                res4 = res4.json()
                if res4['code'] != 0:
                    fail_reason = 'uid管理系统生成Company Serial发生异常'
                    return self.failResponse(company.name, quantity, fail_reason, Log, response)

                return response.json(0)

        except Exception as e:
            djangoLogger = logging.getLogger('django')
            djangoLogger.exception(repr(e))
            return response.json(176)

    def create_serial(self, request_dict, response, request):
        """
        生成序列号
        :param request_dict: 请求参数
        :param response: 响应对象
        :param request: 请求
        :return:
        """
        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))

            # 记录操作日志
            ip = CommonService.get_ip_address(request)
            content = json.loads(json.dumps(request_dict))
            log = {
                'ip': ip,
                'user_id': 1,
                'status': 200,
                'time': now_time,
                'url': 'serialNumber/create',
                'content': json.dumps(content),
                'operation': '生成{}个Serial Number{}: {}'.format(quantity, '成功', '同步更新成功'),
            }

            # 开启事务写入
            with transaction.atomic():
                SerialNumberModel.objects.bulk_create(serial_number_bulk)
                Log = LogModel.objects.create(**log)

                # 同步更新业务服务器和uid管理系统的序列号表
                url1 = 'http://test.zositechc.cn/serialNumber/create'
                url2 = 'https://www.zositechc.cn/serialNumber/create'
                url3 = 'http://www.dvema.com/serialNumber/create'
                url4 = 'http://www.zositeche.com/company/createSerial'
                requests_data = {'quantity': quantity}

                res1 = requests.post(url=url1, data=requests_data, timeout=2 * 60)
                if res1.status_code != 200:
                    fail_reason = '请求测试服务器生成Serial Number响应状态码异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                res1 = res1.json()
                if res1['result_code'] != 0:
                    fail_reason = '测试服务器生成Serial Number发生异常'
                    return self.failResponse(quantity, fail_reason, Log, response)

                res2 = requests.post(url=url2, data=requests_data, timeout=2 * 60)
                if res2.status_code != 200:
                    fail_reason = '请求国内服务器生成Serial Number响应状态码异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                res2 = res2.json()
                if res2['result_code'] != 0:
                    fail_reason = '国内服务器生成Serial Number发生异常'
                    return self.failResponse(quantity, fail_reason, Log, response)

                res3 = requests.post(url=url3, data=requests_data, timeout=2 * 60)
                if res3.status_code != 200:
                    fail_reason = '请求国外服务器生成Serial Number响应状态码异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                res3 = res3.json()
                if res3['result_code'] != 0:
                    fail_reason = '国外服务器生成Serial Number发生异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                res4 = requests.post(url=url4, data=requests_data, timeout=2 * 60)
                if res4.status_code != 200:
                    fail_reason = '请求uid管理系统生成Company Serial响应状态码异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                res4 = res4.json()
                if res4['code'] != 0:
                    fail_reason = 'uid管理系统生成Company Serial发生异常'
                    return self.failResponse(quantity, fail_reason, Log, response)
                return response.json(0)

        except Exception as e:
            djangoLogger = logging.getLogger('django')
            djangoLogger.exception(repr(e))
            return response.json(176)

    def failResponse(self, quantity, fail_reason, Log, response):
        operation = '生成{}个Serial Number{}: {}'.format(quantity, '失败', fail_reason)
        Log.operation = operation
        Log.save()
        return response.json(177)

    def do_quantity(self, request_dict, userID, response):
        """
        查询当前可用的UID的数量
        :param request_dict: 请求参数
        :param userID: 响应对象
        :param response: 请求
        :return:
        """
        user_qs = UserModel.objects.filter(id=userID)
        if not user_qs.exists():
            return response.json(9)

        unused_serial_number_count = SerialNumberModel.objects.filter(use_status=0).count()
        remain_qs = CompanySerialModel.objects.filter(status=1).count()
        company_qs = CompanyModel.objects.values('id', 'name')
        company_serial_list = []    # 剩余已分配未使用的序列号数量
        company_not_used_list = []   # 已使用的序列号数量
        for company in company_qs:
            id = company['id']
            name = company['name']
            not_used_qs = CompanySerialModel.objects.filter(status=3, company_id=id).count()
            res = {
                'name': name,
                'number': not_used_qs
            }
            company_not_used_list.append(res)

        for company in company_qs:
            id = company['id']
            name = company['name']
            company_used_qs = CompanySerialModel.objects.filter(status=1, company_id=id).count()
            res = {
                'name': name,
                'numbers': company_used_qs
            }
            company_serial_list.append(res)
        res_data = {'code': 0, 'companyRemainCount': company_not_used_list, 'companyRemain': company_serial_list,
                    'unused_serial_number_count': unused_serial_number_count,
                    'unused_all_count': remain_qs}
        return response.json(0, {'data': res_data})

    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))

    # 分配序列号
    @transaction.atomic
    def do_allot(self, request_dict, response):
        username = request_dict.get('username', None)
        quantity = int(request_dict.get('quantity', None))
        token = request_dict.get('token', None)

        token = TokenObject(token)
        if token.code != 0:
            return response.json(token.code)
        user = UserModel.objects.get(id=token.userID)
        if not user or '0' not in user.permission:
            return response.json(404)

        # 要分配的对象
        allot_user_qs = UserModel.objects.filter(username=username)
        if not allot_user_qs.exists():
            return response.json(444, 'username')

        # 取出对应区域可用的UID分配给allot_user
        sn_qs = SerialNumberModel.objects.filter(use_status=3)[0:quantity]
        sns = []
        for sn in sn_qs:
            sns.append(sn.serial_number)

        cs_qs = CompanySerialModel.objects.filter(serial_number__in=sns, status=3)

        sns = []
        for cs in cs_qs:
            sns.append(cs.serial_number)
        sn_qs = SerialNumberModel.objects.filter(serial_number__in=sns)

        count = sn_qs.count()
        if count < quantity:
            return response.json(444, '序列号不足')
        try:
            updates = []
            datas = []
            count = 0
            if sn_qs.exists():
                sn_qs = sn_qs[0:quantity]
                now_time = int(time.time())
                for i in range(len(sn_qs)):
                    item = sn_qs[i]
                    serialNumberModel = SerialNumberModel(
                        id=item.id,
                        serial_number=item.serial_number,
                        status=item.status,
                        use_status=1,
                        add_time=item.add_time
                    )
                    CompanySerialModel.objects.filter(serial_number=item.serial_number).update(status=1)
                    user_serial_number = UserSerialNumberModel()
                    user_serial_number.serial_number = serialNumberModel
                    user_serial_number.user = allot_user_qs[0]
                    user_serial_number.add_time = now_time
                    user_serial_number.update_time = now_time

                    datas.append(user_serial_number)
                    updates.append(serialNumberModel)

                    if len(updates) % 5000 == 0:
                        bulk_update(updates)
                        UserSerialNumberModel.objects.bulk_create(datas)

                if len(updates) > 0:
                    bulk_update(updates)
                    UserSerialNumberModel.objects.bulk_create(datas)

                del datas
                del updates
                return response.json(0)
            else:
                return response.json(444)
        except Exception as e:
            print(e)
            return response.json(500)

    # 提供给pc端获取序列号
    def do_get_serial_number(self, request_dict, request, response):
        quantity = 1  # 只能取一个
        company_id = request_dict.get('company_id', None)
        token = request_dict.get('token', None)
        time_stamp = request_dict.get('time_stamp', None)
        mac = request_dict.get('mac', None)

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

        token = int(CommonService.decode_data(token))
        time_stamp = int(time_stamp)

        now_time = int(time.time())
        distance = now_time - time_stamp

        if token != time_stamp or distance > 60000 or distance < -60000:  # 为了全球化时间控制在一天内
            return response.json(404)

        redisObj = RedisObject()
        # redis加锁,防止同时进行其他操作
        serial_operate_lock_key = 'serial_operate_lock'
        isLock = redisObj.CONN.setnx(serial_operate_lock_key, 1)
        if not isLock:
            return response.json(5)
        redisObj.CONN.expire(serial_operate_lock_key, 60)

        user_qs = UserModel.objects.filter(id=3)
        if not user_qs.exists():
            redisObj.del_data(key=serial_operate_lock_key)  # redis解锁
            return response.json(9)

        user = user_qs[0]
        result = UserSerialNumberModel.objects.filter(user__id=user.id, serial_number__use_status=1). \
            aggregate(num=Count('serial_number__use_status'))

        us_qs = UserSerialNumberModel.objects.filter(user__id=user.id, serial_number__use_status=1). \
            values('serial_number__serial_number')

        sns = []
        for us in us_qs:
            sns.append(us['serial_number__serial_number'])

        cs_qs = CompanySerialModel.objects.filter(serial_number__in=sns, status=1, company__secret=company_id).values(
            'serial_number', 'company__mark')
        sns = []
        mark = ""
        if cs_qs.exists():
            for cs in cs_qs:
                sns.append(cs['serial_number'])
            mark = cs_qs[0]['company__mark']

        # 判断是否还有可用的序列号
        count = result['num']
        sn_qs = SerialNumberModel.objects.filter(serial_number__in=sns)
        if not sn_qs.exists() or count < quantity:
            redisObj.del_data(key=serial_operate_lock_key)  # redis解锁
            return response.json(444, '序列号不足')

        try:
            with transaction.atomic():  # 开启事务
                sn_qs = sn_qs[0: quantity]
                serial_number = sn_qs[0].serial_number
                # 防止重复获取序列号
                is_lock = redisObj.CONN.setnx(serial_number + 'serial_number_lock', 1)
                redisObj.CONN.expire(serial_number + 'serial_number_lock', 60)
                if not is_lock:
                    return response.json(5)

                if not mac:  # 不传入则分配mac
                    mac_qs = MacModel.objects.filter().values('id', 'value', 'is_active')[0]
                    if not mac_qs['is_active']:
                        return response.json(175)
                    mac = mac_qs['value']
                    # 绑定mac地址成功后更新mac表
                    next_mac = CommonService.updateMac(mac)  # mac地址值+1;后3个字节为FF时返回None
                    if next_mac:
                        MacModel.objects.filter().update(value=next_mac, update_time=now_time)  # 更新mac表的mac地址值
                    else:
                        MacModel.objects.filter().update(is_active=False, update_time=now_time)

                # 更新序列号表数据
                serialNumberModel = SerialNumberModel(
                    id=sn_qs[0].id,
                    serial_number=serial_number,
                    status=sn_qs[0].status,
                    use_status=2,
                    add_time=now_time
                )
                updates = []
                updates.append(serialNumberModel)
                bulk_update(updates)

                # 更新企业关联序列号状态
                CompanySerialModel.objects.filter(serial_number=serial_number).update(status=2, update_time=now_time)

                # 记录操作日志
                ip = CommonService.get_ip_address(request)
                content = json.loads(json.dumps(request.POST))
                operation = '获取序列号:{}'.format(serial_number)
                log = {
                    'status': 200,
                    'content': json.dumps(content),
                    'ip': ip,
                    'time': now_time,
                    'url': 'serialNumber/getSerial',
                    'operation': operation,
                    'user': user_qs[0]
                }
                LogModel.objects.create(**log)
                redisObj.del_data(key=serial_operate_lock_key)  # redis解锁
                return response.json(0, {'serial_number': serial_number + mark, 'mac': mac})
        except Exception as e:
            redisObj.del_data(key=serial_operate_lock_key)  # redis解锁
            return response.json(500, repr(e))
