import json
import time
from typing import Any,Dict
from cacheout import LFUCache
from lib.proxy import redisClient,ProxyDataPlatform,ProxyUniversalPlatform
from lib.config import cloudProxy
from utils import ReadConf,TimeConversion,FORMAT_TIME,FORMAT_TIME2
from log import app_log

logger = app_log(__name__)
cache = LFUCache()
KEY_FRAME_LOG_PATH = r"/var/log/key-frame-log/"


class Msg(object):
    def __init__(self):
        self._cmd = ""
        self._body = ""
        self._crc = ""
        self._data = ""
        self._reqNum = ""


class LaneSoftwareMsg(Msg):
    def __init__(self):
        super(LaneSoftwareMsg, self).__init__()

    @property
    def body(self):
        return self._data

    @body.setter
    def body(self, value):
        if not isinstance(value, dict):
            raise TypeError(f"车道软件消息必须是一个dict:{type(value)}")
        self._data = value

    @property
    def cmd(self):
        return self._cmd

    @property
    def reqNum(self):
        return self._reqNum

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self,value):
        if not isinstance(value, dict):
            raise TypeError(f"车道软件消息必须是一个dict:{type(value)}")
        self._data = value

    @property
    def crc(self):
        return self._crc


class EtcMsg(Msg):
    def __init__(self):
        super(EtcMsg, self).__init__()

    @property
    def cmd(self):
        return self._cmd

    @cmd.setter
    def cmd(self, value):
        self._cmd = value

    @property
    def reqNum(self):
        return self._reqNum

    @reqNum.setter
    def reqNum(self, value):
        self._reqNum = value

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value

    @property
    def crc(self):
        return self._crc

    @property
    def body(self):
        body = dict(cmd=self._cmd, req_num=self._reqNum, data=self._data)
        body["crc"] = ""
        self._body = body
        return self._body


class LaneSignal(object):
    def __init__(self) -> None:
        self.lane = ""
        self.paySignalTag = "#####"
        self.resSignalTag = "*****"

    
    def ClearPaySignal(self,serviceId):
        redisClient.set(f"{self.paySignalTag}{serviceId}{self.lane}", "")

    def ClearResSignal(self,serviceId):
        redisClient.set(f"{self.resSignalTag}{serviceId}{self.lane}", "")

    def SetOrderSignal(self, data):
        serviceId = data.get("serviceId")
        data = json.dumps(data)
        redisClient.set(f"{self.paySignalTag}{serviceId}{self.lane}", data)

    async def GetRes(self,serviceId):
        startTime = int(time.time())
        while True:
            endTime = int(time.time())
            if endTime - startTime > 3:
                res = "E12-交易超时"
                break
            res = redisClient.get(f"{self.resSignalTag}{serviceId}{self.lane}")
            if res:
                break
        return res
    
# class XgsLaneSignal(object):
#     '''
#     西公所定制
#     '''
#     def __init__(self) -> None:
#         self.lane = ""
#         self.paySignalTag = "#####"
#         self.resSignalTag = "*****"
#         self.res = "E02-未知"

#     def CreateOrderId(self,serviceId):
#         t =  TimeConversion()
#         t.timestamp = int(time.time())
#         t.fmt = "%Y%m%d%H%M%S"
#         return f"{serviceId}{t.ToDate()}{self.lane}"

#     def ClearPaySignal(self,serviceId):
#         redisClient.set(f"{self.paySignalTag}{serviceId}{self.lane}", "")

#     def ClearResSignal(self,serviceId):
#         redisClient.set(f"{self.resSignalTag}{serviceId}{self.lane}", "")

#     def SetOrderSignal(self, data):
#         '''向西工所平台发出扣费申请'''
#         api = "/renren-fast/v1/payRequest"
#         httpProxy = XgsProxy(publicKey="04BAD7E863B5948BCEDFFF44DBD2D05FBCD4BCF524D3E9DD895E8D24061A7957A099E87CF73E7C6EEDBC9C0C5C81E75C41C1A29751994387BB00A180E4928577A1",
#                              privateKey="FC44401C2740A08EAC0288B80BE35EDDD0CAB71A6522CCF05B5DA08CE294F80B")
#         msgData = {
#             "orderNo":data["payNo"],
#             "plateNumber":data["plate"],
#             "plateColor":data["plateColor"],
#             "entryLane":data["entryLane"],
#             "entryTime":data["entryTime"],
#             "exitLane":data["exitLane"],
#             "exitTime":data["exitTime"],
#             "statisticDate":data["statisticDate"],
#             "shiftNum":1,
#             "payAmount":data["fee"],
#             "feeAmount":data["fee"]
#         }
#         resData = httpProxy.post(api,msgData)
#         if httpProxy.isErr:
#             logger.error(f"请求扣费失败:{httpProxy.errMsg}")
#             return -1
#         logger.info(f"resData============>{resData}")
#         #转换结果
#         repStatus = resData.get("repStatus")
#         if repStatus != 0:
#             self.res = "E11-交易失败" #统一支付失败
#             return
#         self.res = "ok"

#     async def GetRes(self,serviceId):
#         '''从西公所服务获取扣费结果'''
#         return self.res

class OrderResultItem(object):
    def __init__(self) -> None:
        self.payNo = ""
        self.transNumber = ""
        self.res = ""
        self.plate = ""
        self.date = ""

    async def save(self):
        data = {
            "payNo": self.payNo,
            "transNumber": self.transNumber,
            "res": self.res,
            "plate": self.plate,
            "_date": self.date
        }
        porxy = ProxyDataPlatform()
        api = "/api/v1/terminal/record/service/transaction/record/res/"
        res = await porxy.post(api,data)
        if porxy.isErr:
            logger.error(f"记录结果时代理异常:{porxy.errMsg}")
            return -1
        return res

class OrderRevokeItem(object):
    def __init__(self,payNo) -> None:
        self.payNo = payNo

    async def revok(self):
        data = {"data":{"isDelete":1},"where":{"payNo":self.payNo}}
        api = "/api/v1/terminal/record/service/update/keep/"
        porxy = ProxyDataPlatform()
        r = await porxy.post(api,data)
        if porxy.isErr:
            logger.error(f"撤销时代理异常:{porxy.errMsg}")
            return -1
        res = "ok" if r > 0 else "E02-未找到此账单"
        return res

    async def save(self,res):
        api = "/api/v1/terminal/record/service/transaction/record/revoke/"
        data = {"payNo":self.payNo,"revokeTime":int(time.time()),"res":res}
        porxy = ProxyDataPlatform()
        await porxy.post(api,data)

class OrderCheckPayItem(object):
    def __init__(self,qs) -> None:
        self.type = qs["type"] #对账类型
        self.date = qs["date"] #对账日期
        self.shift = qs["shift"] #班次
        self.hour = qs["hour"] #小时
        self.lane = qs["lane"] #出口车道号

    async def CheckPay(self):
        where = {"insertDate":self.date} #基本条件
        if self.type == 2:
            where.update({"insertHour":self.hour}) #按小时对账,加入小时参数
        data = {
            "field":["payNo"],
            "where":where
        }
        api = "/api/v1/terminal/record/service/query/keep/"
        porxy = ProxyDataPlatform()
        qs = await porxy.post(api,data)
        if porxy.isErr:
            logger.error(porxy.errMsg)
            return -1
        return qs

class HeartBeat(object):
    def __init__(self) -> None:
        self.pushTime = int(time.time()) #上报时间

    async def push(self,lane:int)->int:
        conf = ReadConf("DEV")
        platform_conf = ReadConf("PLATFORM")
        api = "/api/v1/dev/platform/gas/dev/park/fee/sys/status/push/"
        data = {
            "pushTime":self.pushTime,
            "devType":conf.get("TYPE"),
            "lane":lane,
            "serviceId":conf.get("ADSCRIPTION")
        }
        porxy = ProxyDataPlatform()
        porxy.host = platform_conf.get("HOST")
        porxy.port = platform_conf.get("PORT")
        await porxy.post(api,data)
        if porxy.isErr:
            logger.error(porxy.errMsg)
            return -1
        return 1

class PayResCallback(object):
    def __init__(self,callBackUrl) -> None:
        t =  TimeConversion()
        t.timestamp = int(time.time())
        t.fmt = "%Y-%m-%d"
        self.callBackTime = t.timestamp
        self.callBackDate = t.ToDate()
        self.callBackUrl = callBackUrl
        self.callbacErrorAlarmManger = None
        self.callbackErrorMsgs = []

    
    def SendCallbackErrorAlarm(self):
        for errMsg in self.callbackErrorMsgs:
            self.callbacErrorAlarmManger.msg = f"{self.callbacErrorAlarmManger.msg}\n错误原因:   {errMsg}"
            self.callbacErrorAlarmManger.send()


    async def callback(self,writeOffData:dict)->Any:
        logger.info(f"callbackData:{writeOffData}")
        with open(f"{KEY_FRAME_LOG_PATH}lane{writeOffData.get('exitLane','0')}-{FORMAT_TIME('%Y-%m-%d')}.log","+a") as file:
            print(f"回调时间:{FORMAT_TIME2('%Y-%m-%d %H:%M:%S.%f')[:-3]}",file=file)
            print(f"回调通知三方结构体:\n{json.dumps(writeOffData,indent=4,ensure_ascii=False)}",file=file)
        callbackPorxy = ProxyUniversalPlatform()
        callbackres = await callbackPorxy.post(self.callBackUrl,writeOffData,timeOut=4,isWholeResult=True)
        if callbackPorxy.isErr:
            msg = f"推送三方交易结果时，{callbackPorxy.errMsg}。"
            self.callbackErrorMsgs.append(msg)
            logger.error(msg)
            return -1
        with open(f"{KEY_FRAME_LOG_PATH}lane{writeOffData.get('exitLane','0')}-{FORMAT_TIME('%Y-%m-%d')}.log","+a") as file:
            print(f"回调结果:{FORMAT_TIME2('%Y-%m-%d %H:%M:%S.%f')[:-3]}\n{json.dumps(callbackres,indent=4,ensure_ascii=False)}",file=file)
            print("--------------------------end--------------------------",file=file)
        return callbackres
    
    async def save(self,callBackInfo:dict,callbackConfirmInfo:dict)->Any:
        body = {
            "callback_info":callBackInfo,
            "callback_confirm_info":callbackConfirmInfo
        }
        api = "/api/v1/terminal/record/service/transaction/record/callback/"
        proxy = ProxyDataPlatform()
        res  = await proxy.post(api,body)
        if proxy.isErr:
            msg = f"三方确认保存时，{proxy.errMsg}。"
            self.callbackErrorMsgs.append(msg)
            logger.error(msg)
            return -1
        return res

class KtPayResCallback(object):
    def __init__(self) -> None:
        t =  TimeConversion()
        t.timestamp = int(time.time())
        t.fmt = "%Y-%m-%d"
        self.callBackTime = t.timestamp
        self.callBackDate = t.ToDate()
        self.callBackUrl = ""
    
    async def callback(self,res:Any,order:dict,parkId)->Any:
        t =  TimeConversion()
        t.timestamp = int(time.time())
        t.fmt = "%Y-%m-%d %H:%M:%S"
        data = {
            "orderNo":order["payNo"],
            "payableAmount":order["fee"],
            "payTime":t.ToDate(),
            "amount":order["fee"],
            "payType":1029,
            "payMethod":1008,
            "freeMoney":0,
            "freeTime":0,
            "isCarLeave":1,
            "outOrderNo":order["payNo"],
            # "paymentExt":{},
            "isNoSense":1,
            "serviceCode":"payParkingFee",
            "parkId":parkId
        }

        body = {
            "api":"/api/wec/PayParkingFee",
            "data":data
        }
        callbackPorxy = ProxyDataPlatform()
        callbackPorxy.host = cloudProxy.host
        callbackPorxy.port = cloudProxy.port
        callbackres = await callbackPorxy.post(self.callBackUrl,body,timeOut=2)
        if callbackPorxy.isErr:
            logger.error(callbackPorxy.errMsg)
            return -1
        return 1

    async def save(self,payNo:str,callBackRes:str,callbackErr="")->Any:
        data = {
            "pay_no":payNo,
            "callback_time":self.callBackTime,
            "callback_date":self.callBackDate,
            "callback_status":callBackRes,
            "callback_err":callbackErr
        }
        body = {
            "callback_info":data,
            "callback_confirm_info":{}
        }
        api = "/api/v1/terminal/record/service/transaction/record/callback/"
        proxy = ProxyDataPlatform()
        res  = await proxy.post(api,body)
        if proxy.isErr:
            logger.error(proxy.errMsg)
            return -1
        return res


# class XgsPayResCallback(object):
#     def __init__(self,callBackUrl) -> None:
#         self.callBackUrl = callBackUrl
    
#     def callback(self,writeOffData):
#         logger.info(f"callbackData:{writeOffData}")
#         callbackPorxy = XgsProxy()
#         callbackres = callbackPorxy.post(self.callBackUrl,writeOffData,timeOut=4)
#         if callbackPorxy.isErr:
#             msg = f"推送三方交易结果时，{callbackPorxy.errMsg}。"
#             logger.error(msg)
#             return -1
#         return callbackres

class DrPayResCallback(PayResCallback):
    def __init__(self,callBackUrl) -> None:
        super(DrPayResCallback,self).__init__(callBackUrl)
    
    async def callback(self,res:Any,order:dict,parkId)->Any:
        t =  TimeConversion()
        t.timestamp = int(time.time())
        t.fmt = "%Y-%m-%d %H:%M:%S"
        data = {
            "parkid":parkId,
            "carNo":order["fee"],
            "cardId":"",
            "cardNo":"",
            "entryTime":order[""],
            "payTime":order[""],
            "chargeDuration":0,
            "amount":order["fee"],
            "purchaseAmount":order["fee"],
            "couponAmount":0,
            "paymentType":4,
            "paymentTnx":order["payNo"],
            "operator":"payParkingFee",
            "channelId":parkId
        }

        body = {
            "api":f"/v2/car/temporarycar/payment/parkid/{parkId}",
            "data":data
        }
        callbackPorxy = ProxyDataPlatform()
        callbackPorxy.host = cloudProxy.host
        callbackPorxy.port = cloudProxy.port
        callbackres = await callbackPorxy.post(self.callBackUrl,body,timeOut=2)
        if callbackPorxy.isErr:
            logger.error(callbackPorxy.errMsg)
            return -1
        return 1

class Order(object):
    def __init__(self) -> None:
        self._plate = ""
        self._plateColor = ""
        self._entryLane = ""
        self._exitLane = ""
        self._entryTime = ""
        self._exitTime = ""
        self._statisticDate = ""
        self._shift = 0
        self._fee = ""
        self._payNo = ""

    @property
    def plate(self):
        return self._plate

    @plate.setter
    def plate(self,v):
        assert v,"车牌不能为空"
        self._plate = v

    @property
    def entryLane(self):
        return self._entryLane

    @entryLane.setter
    def entryLane(self,v):
        try:
            v = int(v)
        except Exception as e:
            raise TypeError(f"入口车道号转换异常:{e}")
        assert isinstance(v,int),f"入口车道号类型不正确:需要一个int值,此处获取到:{type(v)}"
        self._entryLane = v

    @property
    def exitLane(self):
        return self._exitLane
    
    @exitLane.setter
    def  exitLane(self,v):
        try:
            v = int(v)
        except Exception as e:
            raise TypeError(f"出口车道号转换异常:{e}")
        assert isinstance(v,int),f"出口车道号类型不正确:需要一个int值,此处获取到{type(v)}"
        self._exitLane = v

    
    @property
    def entryTime(self):
        return self._entryTime

    @entryTime.setter
    def  entryTime(self,v):
        '''设置入场时间'''
        assert v,"入场时间不能为空"
        assert isinstance(v,str),f"入场时间类型不正确:需要一个string值,此处获取到{type(v)}"
        self._entryTime = v

    
    @property
    def exitTime(self):
        return self._exitTime

    @exitTime.setter
    def  exitTime(self,v):
        '''设置出场时间'''
        assert v,"出场时间不能为空"
        assert isinstance(v,str),f"出场时间类型不正确:需要一个string值,此处获取到{type(v)}"
        self._exitTime = v

    @property
    def statisticDate(self):
        return self._statisticDate

    @statisticDate.setter
    def  statisticDate(self,v):
        '''设置对账统计时间'''
        assert v,"对账统计时间不能为空"
        assert isinstance(v,int),f"对账统计时间类型不正确:需要一个int值,此处获取到{type(v)}"
        self._statisticDate = v


    @property
    def shift(self):
        return self._statisticDate

    @shift.setter
    def  shift(self,v):
        '''设置班次'''
        assert v,"班次不能为空"
        assert isinstance(v,int),f"班次类型不正确:需要一个int值,此处获取到{type(v)}"
        self._shift = v
    
    @property
    def fee(self):
        return self._fee

    @fee.setter
    def fee(self,v):
        '''设置扣费金额'''
        assert v,f"扣费金额不能为空:{v}"
        assert v > 0 ,f"扣费金额不合法:{v}"
        assert isinstance(v,int),f"扣费金额类型不正确:需要一个int值,此处获取到{type(v)}"
        self._fee = v

    @property
    def payNo(self):
        return self._payNo

    @payNo.setter
    def payNo(self,v):
        '''设置订单号'''
        assert v,f"订单号不能为空:{v}"
        assert isinstance(v,str),f"订单号类型不正确:需要一个string值,此处获取到{type(v)}"
        self._payNo = v

    @property
    def plateColor(self):
        return self._plateColor

    @plateColor.setter
    def plateColor(self,v):
        '''设置车牌颜色'''
        assert str(v),f"车牌颜色不能为空:{v}"
        assert isinstance(v,int),f"车牌颜色类型不正确:需要一个int值,此处获取到{type(v)}"
        self._plateColor = v
    
        
    def format(self):
        return dict(
            plate=self._plate,
            plateColor = self._plateColor,
            entryLane = self._entryLane,
            exitLane = self._exitLane,
            entryTime = self._entryTime,
            exitTime = self._exitTime,
            statisticDate = self._statisticDate,
            shift = self._shift,
            fee = self._fee,
            payNo = self._payNo
        )

async def fun():
    s = int(time.time())
    while True:
        e = int(time.time())
        if e - s >=10:break
    print(f"完成:{s}")




class OrderWriteOffManger(object):
    def __init__(self,payResCallback:PayResCallback,orderRevokeItem:OrderRevokeItem,orderResultItem:OrderResultItem) -> None:
        self.payResCallback = payResCallback
        self.orderRevokeItem = orderRevokeItem
        self.orderResultItem = orderResultItem

    async def WriteOffBySavePayRes(self):
        if self.orderResultItem.res in ["E13-未知","E12-交易超时"]:
            # 主动撤销
            revokRes = await self.orderRevokeItem.revok()
            await self.orderRevokeItem.save(revokRes)
            logger.info(f"回调异常主动撤销结果:{revokRes}")
        await self.orderResultItem.save()


    async def SaveCallBackRes(self,callbackRes:Dict[str,Any]):
        #记录回调结果
        callbackConfirmInfo = callbackRes.get("data",{})
        callbackStatus = callbackConfirmInfo.get("status")
        callbackInfo = {
            "pay_no":self.orderResultItem.payNo,
            "callback_time":self.payResCallback.callBackTime,
            "callback_date":self.payResCallback.callBackDate,
            "callback_status":callbackStatus,
            "callback_err":callbackRes.get("err_msg","")
        }
        callbackConfirmData = callbackRes.get("data",{})
        callbackStatus = callbackConfirmData.get("status",-1)
        await self.payResCallback.save(callbackInfo,callbackConfirmInfo)

    async def WriteOffByCallback(self,exitLane,exitTime,fee):
        callBackRes = {}
        writeOffData = {
            "payNo":self.orderResultItem.payNo,
            "callBackTime":self.payResCallback.callBackTime,
            "callBackDate":self.payResCallback.callBackDate,
            "res":self.orderResultItem.res,
            "exitLane":exitLane,
            "exitTime":exitTime,
            "fee":fee,
            "plate":self.orderResultItem.plate
        }
        try:
            callBackRes = await self.payResCallback.callback(writeOffData)
        except Exception as e:
            callBackErr = f"扣费结果回调异常:{e}"
            logger.error(callBackErr)
            revokRes = await self.orderRevokeItem.revok()
            await self.orderRevokeItem.save(revokRes)
            logger.info(f"回调异常主动撤销结果:{revokRes}")

        if callBackRes == -1:
            # 主动撤销
            callBackErr = f"扣费结果回调失败:{callBackRes}"
            callBackRes = {}
            logger.error(callBackErr)
            revokRes = await self.orderRevokeItem.revok()
            await self.orderRevokeItem.save(revokRes)
            logger.info(f"回调失败主动撤销结果:{revokRes}")

        await self.SaveCallBackRes(callBackRes)
        self.payResCallback.SendCallbackErrorAlarm()

# class XgsOrderWriteOffManger(OrderWriteOffManger):
#     def __init__(self, payResCallback: PayResCallback,xgsResCallback:XgsPayResCallback,orderRevokeItem: OrderRevokeItem, orderResultItem: OrderResultItem) -> None:
#         super(XgsOrderWriteOffManger,self).__init__(payResCallback, orderRevokeItem, orderResultItem)
#         self.xgsResCallback = xgsResCallback

#     async def WriteOffByCallback(self,exitLane,exitTime,fee):
#         callBackRes = {}
#         writeOffData = {
#             "payNo":self.orderResultItem.payNo,
#             "callBackTime":self.payResCallback.callBackTime,
#             "callBackDate":self.payResCallback.callBackDate,
#             "res":self.orderResultItem.res,
#             "exitLane":exitLane,
#             "exitTime":exitTime,
#             "fee":fee,
#             "plate":self.orderResultItem.plate
#         }
#         try:
#             callBackRes = await self.payResCallback.callback(writeOffData)
#         except Exception as e:
#             callBackErr = f"扣费结果回调异常:{e}"
#             logger.error(callBackErr)
#             revokRes = await self.orderRevokeItem.revok()
#             await self.orderRevokeItem.save(revokRes)
#             logger.info(f"回调异常主动撤销结果:{revokRes}")
#             return

#         if callBackRes == -1:
#             # 主动撤销
#             callBackErr = f"扣费结果回调失败:{callBackRes}"
#             callBackRes = {}
#             logger.error(callBackErr)
#             revokRes = await self.orderRevokeItem.revok()
#             await self.orderRevokeItem.save(revokRes)
#             logger.info(f"回调失败主动撤销结果:{revokRes}")
#             return
        
#         await self.SaveCallBackRes(callBackRes)
#         self.payResCallback.SendCallbackErrorAlarm()
        
        
#         # 向西公所确认订单
#         t =  TimeConversion()
#         t.timestamp = int(time.time())
#         t.fmt = "%Y%m%d%H%M%S"
#         xgsWriteOffData = {
#             "orderNo": self.orderResultItem.payNo,
#             "confirmTime":t.ToDate() ,
#             "confirmType": 1
#         }
#         try:
#             xgsBackRes = self.xgsResCallback.callback(xgsWriteOffData)
#             logger.info(f"xgsBackRes==============>{xgsBackRes}")
#         except Exception as e:
#             pass