import json
import time

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

from AgentModel.models import DeviceCustomUID, CustomUIDPool
from Ansjer.config import LOGGER
from Model.models import LogModel
from Object.RedisObject import RedisObject
from Object.ResponseObject import ResponseObject
from Service.CommonService import CommonService


class DeviceCustomUIDView(View):
    def get(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        return self.validation(request.GET, request, operation)

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

    def delete(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        delete = QueryDict(request.body)
        if not delete:
            delete = request.GET
        return self.validation(delete, request, operation)

    def put(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        put = QueryDict(request.body)
        return self.validation(put, request, operation)

    def validation(self, request_dict, request, operation):
        response = ResponseObject('en')
        if operation == 'getUID':  # 获取电池电量列表
            return self.get_custom_uid(request, request_dict, response)

    @staticmethod
    def get_custom_uid(request, request_dict, response):
        ip = CommonService.get_ip_address(request)
        LOGGER.info(f'获取定制客户UID:{request_dict},ip:{ip}')
        mac = request_dict.get('mac')
        token = request_dict.get('token')
        time_stamp = request_dict.get('time_stamp')

        # 参数校验
        if not all([mac, token, time_stamp]):
            LOGGER.error(f'{mac}请求绑定uid参数缺失')
            return response.json(444)

        # 时间戳token校验
        if not CommonService.check_time_stamp_token(token, time_stamp):
            LOGGER.error(f'{mac}时间戳校验失败time:{time_stamp},tk:{token}')
            return response.json(13)

        redis = RedisObject(db=3)
        lock_key = f"MAC:ASSIGN:{mac}"
        request_id = f"{mac}_{int(time.time())}"

        try:
            # 预检查绑定状态（缓存优化）
            cached_uid = redis.get_data(f"DEVICE:MAC:{mac}")
            if cached_uid:
                return response.json(0, data={'uid': cached_uid})

            # 分布式锁配置（增加锁持有检测）
            acquired = False
            for _ in range(3):
                if redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1):
                    acquired = True
                    break
                time.sleep(0.3)
            if not acquired:
                LOGGER.warning(f'{mac}系统繁忙请稍后重试')
                return response.json(5)
            with transaction.atomic():
                # 二次检查（带数据库查询）
                existing = DeviceCustomUID.objects.filter(
                    device_mac=mac,
                    status=1
                ).only('uid').first()
                if existing:
                    # 更新缓存
                    redis.set_ex_data(f"DEVICE:MAC:{mac}", existing.uid, 10)
                    return response.json(0, {'uid': existing.uid})
                count = 0
                while count < 5:
                    available_uid = (
                        CustomUIDPool.objects
                            .filter(status=0)
                            .order_by('updated_time')
                            .first()
                    )

                    if not available_uid:
                        LOGGER.error(f'{mac}UID池资源耗尽')
                        return response.json(173)
                    # 查询到未被绑定的UID进行redis加锁
                    uid_lock = redis.try_lock(f"DEVICE:UID:{available_uid.uid}",
                                              request_id, expire=3, time_unit_second=1)
                    if not uid_lock:
                        count += 1
                        continue
                    # 原子操作更新
                    rows_updated = CustomUIDPool.objects.filter(
                        id=available_uid.id,
                        status=0  # 乐观锁校验
                    ).update(
                        status=1,
                        updated_time=int(time.time())
                    )

                    if int(rows_updated) <= 0:  # 更新失败
                        count += 1
                        continue

                    if not rows_updated:
                        LOGGER.warning(f'{mac}UID并发竞争更新失败')
                        return response.json(14)
                    # 创建设备关联记录
                    DeviceCustomUID.objects.create(
                        device_mac=mac,
                        uid=available_uid.uid,
                        status=1,
                        updated_time=int(time.time()),
                        created_time=int(time.time())
                    )

                    content = json.loads(json.dumps(request_dict))
                    log = {
                        'ip': ip,
                        'user_id': 1,
                        'status': 200,
                        'time': int(time.time()),
                        'content': json.dumps(content),
                        'url': 'api/device/custom/getUID',
                        'operation': '定制设备mac{}绑定uid: {}'.format(mac, available_uid.uid),
                    }
                    LogModel.objects.create(**log)

                    # 设置缓存
                    redis.set_ex_data(f"MAC_UID_CACHE:{mac}", available_uid.uid, 10)
                    return response.json(0, {'uid': available_uid.uid})

        except Exception as e:
            LOGGER.error(f'mac:{mac}系统异常: {str(e)}', exc_info=True)
            return response.json(500)
        finally:
            redis.release_lock(lock_key, request_id)
