import binascii
import time
from collections import namedtuple
from struct import Struct
from enum import Enum
from lib.req import ReqOperate
from lib.frame import TerFrame
from lib.struct import Mac2,SwitchOrder
from lib.push import PushDevOnline
from lib.definition import ONLINE_KEY_FIELD,ONLINE_KEY_FMT,PARAM_FIELD,PARAM_FMT,ISSUED_MAP,ResSelect
from utils import FORMAT_TIME
from log import Logger
logger = Logger(__name__)

class HandlerUtils(object):
    def __init__(self) -> None:
        pass

    @staticmethod
    def ToNameedtuple(param:bytes,fieldsEnum:Enum,fmtEnum:Enum):
        nt = namedtuple("object",fieldsEnum.value)
        return nt(*Struct(fmtEnum.value).unpack(param))

    @staticmethod
    def IsQuotaTheLimit(fee:int)->bool:
        if fee >= 5000:
            return True
        return False
    
    @staticmethod
    def DiffPlate(vehiclePlate,orderPlate):
        if vehiclePlate == orderPlate:
            return True
        return False
    
    @staticmethod
    def IsExpired(expiredDate):
        day = int(FORMAT_TIME("%Y%m%d"))
        return True if day >= int(expiredDate) else False
    
    @staticmethod
    def GetIssuedCity(issued):
        # cardParam.issued.hex()
        issuedKey = binascii.unhexlify(issued[slice(0, 8)]).decode('gb2312')  #修正卡发行标识
        issued = ISSUED_MAP.get(issuedKey)
        return issued

    @staticmethod
    def OnPlateRemoveRedundant(plateHexStr:str):
        '''去除车牌杂乱字符'''
        return repr(binascii.unhexlify(plateHexStr).decode('gbk')).split('\\', 1)[0].replace('\'','')

    @staticmethod
    def CorrectCardNetworkNumber(cardParam,issuedCity):
        return cardParam._replace(cardNetworkNumber=Struct("!2s").pack(bytes.fromhex(issuedCity + cardParam.cardNetworkNumber.hex()[2:])))
        
    @staticmethod
    def CorrectPlate(vehicleInfo,vehiclePlate):
        return vehicleInfo._replace(plate=Struct("!12s").pack(vehiclePlate.encode('gbk'))) #除杂后修正车牌号

async def HANDLER_FIND_OBU_SIGNAL(stream, cache,handlerCache, rf,reqOperate:ReqOperate,**kwargs):
    '''搜索OBU信号'''
    logger.info(f"--------IN E4 TIME:{time.time()}")
    data = rf.data
    status, = Struct("!s").unpack(data[0:1])
    if status.hex() != "00":
        return ResSelect.E02
    
    keyFrameLogPath = kwargs['keyFrameLogPath']
    lane = kwargs['lane']
    rsuId = kwargs['rsuId']
    parkId = kwargs['parkId']
    isEnablePlatesDiff = kwargs['isEnablePlatesDiff']

    with open(f"{keyFrameLogPath}lane{lane}-{FORMAT_TIME('%Y-%m-%d')}.log","+a") as file:
        print(f"扫描帧:\n{data.hex()}",file=file)
    
    order = handlerCache.get("order")
    workKey = cache.get("workKey")

    frameDataParam = HandlerUtils.ToNameedtuple(data[1:],ONLINE_KEY_FIELD.STEP1,ONLINE_KEY_FMT.STEP1)
    handlerCache.set("obuId", frameDataParam.obuMac.hex())

    # 解析系统信息
    dataContentByOBU = HandlerUtils.ToNameedtuple(frameDataParam.obuInfo,PARAM_FIELD.PARAM_FIELD_BY_OBU_INF0,PARAM_FMT.FMT_BY_OBU)
 
    # 判断OBU合同是否过期
    if HandlerUtils.IsExpired(dataContentByOBU.expiredDate.hex()):
        return ResSelect.E16
    
    dataContentByCiphertextVehicle = HandlerUtils.ToNameedtuple(frameDataParam.vehicleInfo,PARAM_FIELD.PARAM_FIELD_BY_VEHICLE_INF0,PARAM_FMT.FMT_BY_VEHICLE)
    futureVehicleRes = reqOperate.ReqVehicleInfo(
        rsuId,
        dataContentByCiphertextVehicle.ciphertext.hex(),
        dataContentByCiphertextVehicle.random.hex(),
        dataContentByOBU.agrNumber.hex(),
        dataContentByOBU.serviceCode.hex()[0:8],
        workKey["key"]
    )
    dataContentByCard = HandlerUtils.ToNameedtuple(frameDataParam.cardInfo,PARAM_FIELD.PARAM_FIELD_BY_CARD_INF0,PARAM_FMT.FMT_BY_CARD)
    # p = HandlerUtils.OnPlateRemoveRedundant(dataContentByCard.plate.hex())
    # print(f"搜索到车辆:{p}")
    # arrearsRes = await ReqArrearsInfo(p,1) # 查询车辆欠费信息
    # # arrearsRes = [{
    # #         "parkCode":"1012204015DN06FFVMEWX",
    # #         "plateNo":"陕A3F91Z",
    # #         "plateColor":1,
    # #         "totalFee":1,
    # #         "arrearsFee":1,
    # #         "arrearsTime":1704792799430,
    # #         "berthCode":"1042206175TB6W829E4N6",
    # #         "billCode":"2783590",
    # #         "inUnid":"ALARM1189061833474277377"
    # #     }]
    # print(f"查询到车辆欠费信息:{arrearsRes}")
    # if not arrearsRes or arrearsRes == -1:
    #     return ResSelect.E02
    # sw = SwitchOrder()
    # sw.OtherOrderBodyToOrder(1,arrearsRes[0])
    # order = sw.format()
    # print(order)
    # handlerCache.set("order",order)

    # 判定超额消费
    if HandlerUtils.IsQuotaTheLimit(order["fee"]):
        return ResSelect.E17
    
    # 判断卡是否过期
    cardEx = HandlerUtils.IsExpired(dataContentByCard.endTime.hex())
    if cardEx:
        return ResSelect.E16

    issuedCity = HandlerUtils.GetIssuedCity(dataContentByCard.issued.hex())
    if not issuedCity:
        logger.error(f"读取到不明省份车辆:{issuedCity}")
        return ResSelect.E08
    try:
        dataContentByCard = HandlerUtils.CorrectCardNetworkNumber(dataContentByCard,issuedCity)
    except Exception as e:
        logger.error(f"卡网络编号修正异常:{e}")
        return ResSelect.E13

    # 异步卡信息初始化完成就发出黑名单请求
    futureBalckListRes = reqOperate.ReqBlackList(
        parkId,
        dataContentByCard.cardNumber.hex(),
        dataContentByOBU.agrNumber.hex(),
        dataContentByCard.cardNetworkNumber.hex()
    )
    #异步上传关键帧
    # futurePushRes = PushKeyFrame(order["payNo"],order["plate"],data.hex())
    try:
        vehicleRes = await futureVehicleRes
    except Exception as e:
        logger.error(f"车辆解密异常:{e}")
        return ResSelect.E15

    if vehicleRes == -1:
        logger.error("车辆解密失败")
        return ResSelect.E15
    try:
        balckListRes = await futureBalckListRes
    except Exception as e:
        logger.error(f"黑名单请求异常:{e}")
        return ResSelect.E15

    if balckListRes == -1:
        logger.error("黑名单请求失败")
        return ResSelect.E15

    if balckListRes != "0":
        logger.warning(f"车辆黑名单内:{balckListRes}")
        return ResSelect.E09
    # try:
    #     await futurePushRes
    # except Exception as e:
    #     logger.error(f"上传关键帧异常:{e}")
    try:
        dataContentByVehicle = HandlerUtils.ToNameedtuple(bytes.fromhex(vehicleRes['vehInfo'][0:118]), #只要前118个字符59个字节
        PARAM_FIELD.PARAM_FIELD_BY_VEHICLE_FIELD,PARAM_FMT.FMT_BY_VEHICLE_PARAM)
    except Exception as e:
        logger.error(f"格式化参数异常:{e}")
        return ResSelect.E13

    try:
        plateforVehicle = HandlerUtils.OnPlateRemoveRedundant(dataContentByVehicle.plate.hex())
        dataContentByVehicle = HandlerUtils.CorrectPlate(dataContentByVehicle,plateforVehicle)
    except Exception as e:
        logger.error(f"车牌除杂修正异常:{e}")
        return ResSelect.E13
    
    if isEnablePlatesDiff == 1:
        if not HandlerUtils.DiffPlate(plateforVehicle,order["plate"]):
            return ResSelect.E07
        
    tf = TerFrame(b'\x51',(order['fee'],bytes.fromhex(workKey['termId'])),"!i6s")
    tf.rsctl = rf.rsctl
    handlerCache.set_many(dict(
        dataContentByOBU=dataContentByOBU,
        dataContentByVehicle=dataContentByVehicle, 
        dataContentByCard=dataContentByCard))
    stream.write(tf.frame)
    return ResSelect.E00


async def HANDLER_GET_MAC(stream, cache,handlerCache, rf,reqOperate:ReqOperate,**kwargs):
    '''获取mac2'''
    logger.info(f"--------IN E7 TIME:{time.time()}")
    data = rf.data
    status, = Struct("!s").unpack(data[0:1])
    if status.hex() != "00":
        return ResSelect.E14

    rsuId = kwargs['rsuId']

    ORDER = handlerCache.get("order")
    workKey = cache.get("workKey")
    dataContentByCard = handlerCache.get("dataContentByCard")
    # |①OBU物理地址(mac)|②消费前余额|③伪随机数|⑤卡片交易序号|
    frameDataParam = HandlerUtils.ToNameedtuple(data[1:len(data)],ONLINE_KEY_FIELD.STEP2,ONLINE_KEY_FMT.STEP2)
    transacDate = FORMAT_TIME("%Y%m%d")
    transacTime = FORMAT_TIME("%H%M%S")
    transactionTime = f"{transacDate}{transacTime}"
    feeHex = Struct("!i").pack(ORDER['fee']).hex()
    futureMacRes =  reqOperate.ReqMac2(rsuId,transactionTime,frameDataParam.passtypeRand,frameDataParam.cardSN,feeHex,dataContentByCard,workKey['key'])

    handlerCache.set("transactionTime", transactionTime)
    handlerCache.set("transacTime",transacTime)
    handlerCache.set("cardSN", frameDataParam.cardSN)
    handlerCache.set("passtypeRand", frameDataParam.passtypeRand)
    handlerCache.set("preBalance",frameDataParam.banlance)
    macRes = await futureMacRes
    if macRes == -1:
        logger.error("mac请求异常")
        return ResSelect.E11
    mac = Mac2(macRes["mac"])
    handlerCache.set("termNo", mac.termNo)
    tf = TerFrame(
        b'\x52',
        (
            bytes.fromhex(mac.termNo),
            bytes.fromhex(transactionTime),
            bytes.fromhex(mac.mac2)
        ),"!4s7s4s")
    tf.rsctl = rf.rsctl
    stream.write(tf.frame)
    return ResSelect.E00

async def HANDLER_OVER_PAY(stream, cache,handlerCache, rf,reqOperate:ReqOperate,**kwargs):
    '''完成消费电子记账'''
    logger.info(f"--------IN E5 TIME:{time.time()}")
    data = rf.data
    status, = Struct("!s").unpack(data[0:1])
    if status.hex() != "00":
        return ResSelect.E14
    frameDataParam = HandlerUtils.ToNameedtuple(data[1:len(data)],ONLINE_KEY_FIELD.STEP3,ONLINE_KEY_FMT.STEP3)
    handlerCache.set("E5_frameDataParam",frameDataParam)
    return ResSelect.E00
    

async def HANDLER_HEARTBEAT(stream, cache,handlerCache, rf,reqOperate:ReqOperate,**kwargs):
    '''天线心跳'''
    # 增加状态上报机制 每十个心跳单位一次
    devStatus = cache.get("devStatus")
    devNumber = kwargs["devNumber"]
    if not devStatus:
        await PushDevOnline(devNumber)
        cache.set("devStatus", "ok", ttl=60)
    return ResSelect.E00

async def HANDLER_SET_NUM(stream, cache,handlerCache,rf,reqOperate:ReqOperate,**kwargs):
    '''设置功率'''
    status, = Struct("!s").unpack(rf.data)
    if status.hex() != "00":
        logger.error(f"设置功率时状态异常:{status.hex()}")
        return ResSelect.E14

    logger.info("设置功率完成...")
    tf = TerFrame(b'\x19',(1,),"!b")
    stream.write(tf.frame)
    return ResSelect.E00

async def HANDLER_SET_CHANNEL(stream, cache,handlerCache,rf,reqOperate:ReqOperate,**kwargs):
    '''设置信道'''
    status, = Struct("!s").unpack(rf.data)
    if status.hex() != "00":
        logger.error(f"设置功率时状态异常:{status.hex()}")
        return ResSelect.E14
    logger.info("设置信道完成...")
    return ResSelect.E00




HADLER_MAP = {
    "98": HANDLER_SET_NUM,# 设置档位
    "99": HANDLER_SET_CHANNEL,# 设置信道
    "d0": HANDLER_FIND_OBU_SIGNAL,  # 继续交易
    "d1": HANDLER_GET_MAC,  # 计算MAC2
    "d2": HANDLER_OVER_PAY,  # 消费
    "91": HANDLER_HEARTBEAT  # 心跳
}
