import json

from device_data_op.models import TableAllDevCmdDefine, TableDevCmdNamePoll
from device_data_op.serializers import TableAllDevCmdDefineSerializer, TableDevCmdNamePollSerializer
from django.db.models import QuerySet
from rest_framework.response import Response
from rest_framework import status
from .models import (AllDevCmdDefineAndVersion, AllProtocolDefinAndVersion,
                     AllProtocolVersion, CurrentDevVersion)
from .serializers import (AllDevCmdDefineAndVersionSerializer, AllProtocolDefinAndVersionSerializer,
                         AllProtocolVersionSerializer, CurrentDevVersionSerializer)


INIT_VERSION = "init"


def init_protocol_version_manage(protocol_name: str) -> None:
    """
    将协议版本信息初始化，即先将 device 已经在用的表的数据添加到数据库表中
    
    :param protocol_name: 协议名
    """
    protocol_cmds = TableDevCmdNamePoll.objects.filter(protocol_name=protocol_name).all()
    protocol_cmds_serializer = TableDevCmdNamePollSerializer(protocol_cmds, many=True)
    all_protocol_objects: list[AllProtocolDefinAndVersion] = []
    for protocol in protocol_cmds_serializer.data:
        protocol['version'] = INIT_VERSION
        del protocol['id']
        all_protocol_objects.append(AllProtocolDefinAndVersion(**protocol))
    AllProtocolDefinAndVersion.objects.bulk_create(all_protocol_objects)

    cmd_fields: list[QuerySet] = []
    for protocol in protocol_cmds:
        cmd_fields.extend(TableAllDevCmdDefine.objects.filter(cmd_name=protocol.cmd_name).all())
    cmd_fields_serializer = TableAllDevCmdDefineSerializer(cmd_fields, many=True)
    all_cmd_fields_objects: list[AllDevCmdDefineAndVersion] = []
    for cmd_field in cmd_fields_serializer.data:
        cmd_field['version'] = INIT_VERSION
        del cmd_field['id']
        all_cmd_fields_objects.append(AllDevCmdDefineAndVersion(**cmd_field))
    AllDevCmdDefineAndVersion.objects.bulk_create(all_cmd_fields_objects)
    
    version_path = {
        "version": INIT_VERSION
    }
    AllProtocolVersion.objects.create(protocol_name=protocol_name, 
                                      version_paths=json.dumps([version_path]))
    
    CurrentDevVersion.objects.create(protocol_name=protocol_name,
                                     version=INIT_VERSION)


def update_device_protocol_and_cmds(protocol_name: str, version: str) -> Response:
    """
    根据协议名和版本号更新协议版本信息
    
    通过协议名和版本号将 AllProtocolDefinAndVersion 和 AllDevCmdDefineAndVersion 中符合
    要求的数据更新到 TableDevCmdNamePoll 和 TableAllDevCmdDefine 中

    :param protocol_name: 协议名
    :param version: 版本号

    :return: 当前版本，协议相关的数据
    """
    # 更新版本号
    CurrentDevVersion.objects.filter(protocol_name=protocol_name).update(version=version)

    # 更新协议版本信息
    current_version_protocols = AllProtocolDefinAndVersion.objects.filter(protocol_name=protocol_name,
                                                                          version=version).all()
    cmd_fields = []
    for current_version_protocol in current_version_protocols:
        cmd_name = current_version_protocol.cmd_name
        cmd_fields.extend(AllDevCmdDefineAndVersion.objects.filter(cmd_name=cmd_name, version=version).all())
    
    current_version_protocol_serializer = AllProtocolDefinAndVersionSerializer(current_version_protocols, many=True)
    cmd_fields_serializer = AllDevCmdDefineAndVersionSerializer(cmd_fields, many=True)

    # 要先删除这，因为它使用的是 TableDevCmdNamePoll 的旧数据，更新 device 所使用的字段表数据
    try:
        TableAllDevCmdDefine.objects.filter(cmd_name__in=[cmd.cmd_name 
                                                          for cmd in TableDevCmdNamePoll.objects.filter(protocol_name=protocol_name).all()]).delete()
        TableAllDevCmdDefine.objects.bulk_create([TableAllDevCmdDefine(
                                                **{k: v for k, v in cmd_field.items() if k not in ['id', 'version']}) 
                                                for cmd_field in cmd_fields_serializer.data])
    except Exception as e:
        print(e)
        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    # 更新 device 所使用的指令表数据
    try:
        TableDevCmdNamePoll.objects.filter(protocol_name=protocol_name).delete()
        TableDevCmdNamePoll.objects.bulk_create([TableDevCmdNamePoll(
                                                **{k: v for k, v in protocol.items() if k not in ['id', 'version']}) 
                                                for protocol in current_version_protocol_serializer.data])
    except Exception as e:
        print(e)
        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    
    # 返回数据，需要返回生成之后的数据，特别是 id，因此需要重新请求一次
    cmds = []
    new_cmds = TableDevCmdNamePoll.objects.filter(protocol_name=protocol_name).all()
    new_cmds_serializer = TableDevCmdNamePollSerializer(new_cmds, many=True)
    new_cmd_fields = TableAllDevCmdDefine.objects.filter(cmd_name__in=[cmd.cmd_name for cmd in new_cmds]).all()
    new_cmd_fields_serializer = TableAllDevCmdDefineSerializer(new_cmd_fields, many=True)
    for protocol in new_cmds_serializer.data:
        protocol['fields'] = sorted([item for item in new_cmd_fields_serializer.data 
                                     if item['cmd_name'] == protocol['cmd_name']], 
                                     key=lambda item: item['fieldindex'])
        cmds.append(protocol)

    res_data = {
        'current_version': version,
        'protocol': protocol_name,
        'cmds': cmds
    }

    return Response(data=res_data, status=status.HTTP_200_OK)
        

def add_protocol_version_manage(protocol_name: str, version: str, cmds: list) -> Response:
    """
    添加协议版本信息
    
    :param protocol_name: 协议名
    :param version: 版本号
    :param cmds: 协议相关的命令

    :return: 当前版本，协议相关的数据
    """
    # 给协议添加新的版本号
    all_protocol_version_manage = AllProtocolVersion.objects.filter(protocol_name=protocol_name).first()
    version_paths = json.loads(all_protocol_version_manage.version_paths)
    version_paths.append({"version": version})
    all_protocol_version_manage.version_paths = json.dumps(version_paths)
    all_protocol_version_manage.save()

    try:
        # 更新协议下的指令
        for cmd in cmds:
            # 获取指令字段
            fields = cmd.pop('fields')
            # 处理协议指令
            _cmd = AllProtocolDefinAndVersion.objects.filter(protocol_name=protocol_name, cmd_name=cmd['cmd_name']).first()
            new_cmd = AllProtocolDefinAndVersion(**cmd)
            if _cmd == new_cmd:
                # 如果指令已经存在，则更新版本号
                version_list = json.loads(_cmd.version)
                version_list.append(version) if version not in version_list else version_list
                _cmd.version = json.dumps(version_list)
                _cmd.save()
            else:
                # 指令不存在，则存储指令信息
                new_cmd.version = json.dumps([version])
                new_cmd.save()

            # 处理指令
            for field in fields:
                _field = AllDevCmdDefineAndVersion.objects.filter(cmd_name=cmd['cmd_name'], fieldname=field['fieldname']).first()
                new_field = AllDevCmdDefineAndVersion(**field)
                if _field == new_field:
                    # 如果指令字段已经存在，则更新版本号
                    version_list = json.loads(_field.version)
                    version_list.append(version) if version not in version_list else version_list
                    _field.version = json.dumps(version_list)
                    _field.save()
                else:
                    # 指令字段不存在，则存储指令信息
                    new_field.version = json.dumps([version])
                    new_field.save()
    except Exception as e:
        print(e)
        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return Response(status=status.HTTP_200_OK)


def update_protocol_version_manage(protocol_name: str, version: str, cmds: list) -> Response:
    """
    更新协议版本信息
    
    :param protocol_name: 协议名
    :param version: 版本号
    :param cmds: 协议相关的命令

    :return: 当前版本，协议相关的数据
    """
    # 给协议添加新的版本号
     
    all_protocol_version_manage = AllProtocolVersion.objects.filter(protocol_name=protocol_name).first()
    version_paths = json.loads(all_protocol_version_manage.version_paths)
    true_version = False
    for version_path in version_paths:
        if version_path['version'] == version:
            true_version = True
            break
    if not true_version:
        return Response(status=status.HTTP_400_BAD_REQUEST)
    
    try:
        for cmd in cmds:
            # 获取指令字段
            fields = cmd.pop('fields')
            # 处理协议指令
            _cmds = AllProtocolDefinAndVersion.objects.filter(protocol_name=protocol_name, cmd_name=cmd['cmd_name']).all()
            new_cmd = AllProtocolDefinAndVersion(**cmd)
            updated = False
            for _cmd in _cmds:
                # 遍历相同协议下相同名称不同版本的指令
                vesrion_list = json.loads(_cmd.version)
                if version in vesrion_list:
                    if _cmd == new_cmd:
                        updated = True
                        break
                    else:
                        # 该指令还有其他协议版本，新建指令信息
                        if len(vesrion_list) > 1:
                            # 删除原先指令版本信息
                            vesrion_list.remove(version)
                            _cmd.version = json.dumps(vesrion_list)
                            _cmd.save()
                            # 创建新的
                            new_cmd.version = json.dumps([version])
                            new_cmd.save()
                        else:
                            # 更新信息
                            _cmd.cmd_type = cmd['cmd_type']
                            _cmd.encode = cmd['encode']
                            _cmd.timing_cmd_cycle_period = cmd['timing_cmd_cycle_period']
                            _cmd.cmd_explain = cmd['cmd_explain']
                            _cmd.save()

                        updated = True
            if not updated:
                # 如果没有相关指令，则存储指令信息
                new_cmd.version = json.dumps([version])
                new_cmd.save()

            # 处理指令字段
            for field in fields:
                _fields = AllDevCmdDefineAndVersion.objects.filter(cmd_name=cmd['cmd_name'], fieldname=field['fieldname']).all()
                new_field = AllDevCmdDefineAndVersion(**field)
                updated = False
                for _field in _fields:
                    # 遍历相同协议下相同名称不同版本的指令字段
                    version_list = json.loads(_field.version)
                    if version in version_list:
                        if _field == new_field:
                            updated = True
                            break
                        else:
                            # 该指令字段还有其他协议版本，新建指令信息
                            if len(version_list) > 1:
                                # 删除原先指令版本信息
                                version_list.remove(version)
                                _field.version = json.dumps(version_list)
                                _field.save()
                                # 创建新的
                                new_field.version = json.dumps([version])
                                new_field.save()
                            else:
                                # 更新信息
                                _field.cmd_type = field['cmd_type']
                                _field.fieldindex = field['fieldindex']
                                _field.fieldsize = field['fieldsize']
                                _field.value = field['value']
                                _field.minvalue = field['minvalue']
                                _field.maxvalue = field['maxvalue']
                                _field.datatype = field['datatype']
                                _field.operation_in = field['operation_in']
                                _field.operation_in_num = field['operation_in_num']
                                _field.operation_out = field['operation_out']
                                _field.operation_out_num = field['operation_out_num']
                                _field.operabo_in = field['operabo_in']
                                _field.operabo_out = field['operabo_out']
                                _field.lua_script_in = field['lua_script_in']
                                _field.lua_script_out = field['lua_script_out']
                                _field.save()

                            updated = True

                if not updated:
                    # 如果没有相关指令字段，则存储指令信息
                    new_field.version = json.dumps([version])
                    new_field.save()

    except Exception as e:
        print(e)
        return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return Response(status=status.HTTP_200_OK)

