
import { ReqChargingDeductionData, DeductionResponse } from "./ChargingDeduction"
import { TyExternalApiClient, axiosInstance, ApiResponse, throwIfResCodeNoSuccess } from "../refs/nslib"
import { ChargingPileDal } from "./ChargingPileDal"
import { logger } from "mslib";
import { Singleton,Inject } from "typescript-ioc";
import * as uuid from 'uuid'
import * as dayjs from 'dayjs'

/**
 * 抵扣限制和抵扣处理
 * @date 2022-08-16 13:56:23
 * @author 侯洪莉
 */
class ChargingPileService {
    @Inject
    chargingPileDal:ChargingPileDal
    @Inject
    tyExternalApiClient:TyExternalApiClient


    /**
     * 抵扣方式限制判断
     * @param {*}  ReqChargingDeductionData 
     * @返回值 resData：{PileRule,FreeData} PileRule：使用规则 FreeData:可免费金额和时长
     */
    async getChargingLimit(param: ReqChargingDeductionData): Promise<DeductionResponse> {
        let resData = {}
        const { plateNo, parkId,chargingTime } = param
        const pileId=param['pileId']
        // 获取停车信息
        const carplateDataRst = await this.tyExternalApiClient.getParkingPaymentInfo({ parkId: parkId, plateNo: plateNo });
        if (carplateDataRst.resCode != '0') {
            // 获取停车信息失败
            return await DeductionResponse.failWithMsg(carplateDataRst.resMsg);
        }

        const { entryTime, elapsedTime, payable, } = carplateDataRst.data;

        const parkCarInfo = {
            inTime: entryTime,  // 入场时间
            parkTime: elapsedTime * 60, // 停车时长 （秒）
            payMoney: payable, // 待支付金额（分）
        }
        
        // 获取某充电桩品牌的使用规则配置
        const pileRule = await this.chargingPileDal.getPileRule(parkId, pileId);
        if (!pileRule) {
            return await DeductionResponse.failWithMsg(`未配置或未启用该充电桩-${pileId}-使用规则`);
        }
        //返回参数添加当前抵扣规则
        Object.assign(resData, {
            PileRule: pileRule
        })

        if (+pileRule.chargeTime != 0 && chargingTime < +pileRule.chargeTime) {
            return await DeductionResponse.failWithMsg("充电时长未达到最小充电时长要求");
        }

        //抵扣来源为第三方
        if (pileRule.discountSource == 1) {
            if ((!param?.discountNumber && param?.discountNumber != 0) || (!param?.discountType && param?.discountType != 0)) {
                return await DeductionResponse.failRes("当抵扣来源为第三方时，抵扣类型和抵扣数量为必填值", param);
            }
            pileRule.discountNumber = +param.discountNumber
            pileRule.discountType = param.discountType
        }

        //获取全局配置规则
        const configRule = await this.chargingPileDal.getPileConfig(parkId)
        let timeCanUse_c = Infinity //全局限制判断本次可用抵扣时间
        let moneyCanUse_c = Infinity //全局限制判断本次可用抵扣金额 默认不限制全局
        //存在全局配置 判断全局配置的抵扣限制
        if (configRule) {
            let { timeCanUse, moneyCanUse } = await this.getConfigRuleLimit(param, configRule, parkCarInfo)
            if (timeCanUse === 0 || moneyCanUse === 0) {
                return await DeductionResponse.failWithMsg("全局限制未通过，无法抵扣")
            }
            timeCanUse_c = timeCanUse
            moneyCanUse_c = moneyCanUse
        }

        logger.info(`全局配置限制本次可使用 timeCanUse_c-----:${timeCanUse_c},moneyCanUse_c:${moneyCanUse_c}`)

        //判断该充电桩使用规则的限制
        let { timeCanUse: timeCanUse_p, moneyCanUse: moneyCanUse_p } = await this.getPileRuleLimit(param, pileRule, parkCarInfo)
        logger.info(`使用规则限制本次可使用 timeCanUse_p-----:${timeCanUse_p}秒,moneyCanUse_p:${moneyCanUse_p}分`)


        let timeCanUse, moneyCanUse //本次抵扣可使用时间和金额

        //Number.isFinite(Infinity);  => false 当全局和单个使用规则都不限制时 赋值Infinity 无限大不能相减
        if (!Number.isFinite(timeCanUse_p) && !Number.isFinite(timeCanUse_c)) {
            timeCanUse = Infinity
        }
        else {
            timeCanUse = timeCanUse_c - timeCanUse_p >= 0 ? timeCanUse_p : timeCanUse_c //全局可使用和单个可使用 取最小
        }
        if (!Number.isFinite(moneyCanUse_p) && !Number.isFinite(moneyCanUse_c)) {
            moneyCanUse = Infinity
        }
        else {
            moneyCanUse = moneyCanUse_c - moneyCanUse_p >= 0 ? moneyCanUse_p : moneyCanUse_c
        }
        logger.info(`本次可使用 timeCanUse-----:${timeCanUse}秒,moneyCanUse:${moneyCanUse}分`)


        //抵扣时间
        if (pileRule.discountType === 0 && timeCanUse > 0) {
            let time = (pileRule.discountMethod === 0||pileRule.discountSource===1) ? pileRule.discountNumber : chargingTime
            let freeTime
            if (pileRule.discountMethod === 0) {
                freeTime = timeCanUse - time >= 0 ? time : 0

            } else {
                freeTime = timeCanUse - time >= 0 ? time : timeCanUse
            }
            if (freeTime <= 0) {
                return await DeductionResponse.failRes(`抵扣失败，配置的可抵扣值${time}秒>本次可使用值${timeCanUse}秒`, resData);
            }
            Object.assign(resData, {
                FreeData: {
                    freeTime: freeTime, //单位秒
                    freeMoney: 0 //单位分
                }
            })
            return await DeductionResponse.successRes(`可抵扣时间${freeTime}秒`, resData);
        }

        //抵扣金额
        if (pileRule.discountType === 1 && moneyCanUse > 0) {
            let money = moneyCanUse >= pileRule.discountNumber ? pileRule.discountNumber : 0
            if (money <= 0) {
                return await DeductionResponse.failRes(`抵扣失败，配置的可抵扣值${pileRule.discountNumber}分>本次可使用值${moneyCanUse}分`, resData);
            }
            Object.assign(resData, {
                FreeData: {
                    freeTime: 0, //单位秒
                    freeMoney: money //单位分
                }
            })
            return await DeductionResponse.successRes(`可抵扣时间${money}分`, resData);
        }
        Object.assign(resData, {
            timeCanUse: timeCanUse,
            moneyCanUse: moneyCanUse
        })

        return await DeductionResponse.failRes(`抵扣失败，无可用抵扣`, resData);

    }


    /**
     * 车辆类型修改和 充电桩抵扣
     * @param {Object} param  ReqChargingDeductionData
     * @param {FreeData,PileRule} freeData 可抵扣信息
  
     */
    async discount(param, discountData) {
        let { FreeData, PileRule } = discountData
        try {
            //若执行特殊计费规则 修改车牌
            if (PileRule.isSpecialFeeRule == 1) {
                let modifyData = {
                    parkId: param.parkId,
                    plateNo: param.plateNo,
                    carStyle: PileRule.carStyle,
                }

                let modifyRes = await this.tyExternalApiClient.modifyCarInfo(modifyData)
                logger.info('修改车辆类型返回结果',JSON.stringify(modifyRes))
                if (modifyRes.resCode != '0') {
                    return await DeductionResponse.failRes(`车辆类型修改失败--${modifyRes.resMsg}`, { param, discountData });

                }
            }

            //调用充电桩接口进行抵扣
            let reqData = {
                parkId: param.parkId,
                plateNo: param.plateNo,
                startTime:dayjs(param.startTime).format('YYYY-MM-DD HH:mm:ss') ,  //充电开始时间
                endTime:dayjs(param.endTime).format('YYYY-MM-DD HH:mm:ss') , //充电结束时间
                orderNo: param.orderNo,  //订单号
                stationId: param.stationId || uuid.v4().replace(/-/g, ''),
                stationName: param.stationName || '充电桩',    //电站名称
                deviceId: param.deviceId || param.pileId,     //设备Id
                deviceName: param.deviceName || '无',  // 设备名称
                spaceNo: '无',    //车位号
                power: param.power || 0,  //充电量 单位：度，小数点后2位
                elecMoney: param.elecMoney || 0,  //电费 单位 分
                seviceMoney: param.seviceMoney || 0,   //服务费 单位：分
                totalMoney: param.totalMoney || 0, //总费用 单位：分
                freeType: 0,    // 固定为0   0或1    0：根据freemoeny、freeTime减免1：本地车场配置减免规则，根据充电时长换算，freemoney、freetime可以为0
                freeMoney: FreeData.freeMoney,
                freeTime: FreeData.freeTime
            }


            const axiosResponse = await axiosInstance.post<ApiResponse>('/api/wec/SyncChargePilePay', reqData)
            throwIfResCodeNoSuccess(axiosResponse.data)
            let discountRes = axiosResponse.data
            //抵扣成功 且规则配置执行特殊计费规则 则修改车辆类型
            if (discountRes.resCode == '0') {
                return await DeductionResponse.successRes(`抵扣成功`, { param, discountData });
            } else {
                return await DeductionResponse.failRes( `车辆信息修改成功，抵扣失败-${discountRes.resMsg}`, { param, discountData });
            }
        } catch (error) {
            logger.error(`抵扣异常`, error.message)
        }

    }



     /**
     * 获取全局的限制情况
     * @param {Object} params 
     * @param {Object} configRule 全局规则限制
     * @param {Object} parkInfo 停车信息
     * 
     * @return {Int} canUse  可以使用额度 Infinity：表示不限额度
     */
    async getConfigRuleLimit(params, configRule, parkInfo) {
        const { parkId, plateNo } = params;
        // onceMaxNum: 单次抵扣次数上限 dayMaxNum： 每日抵扣次数上限 onceMaxTime:单次停车抵扣时长上限  dayMaxTime:每日抵扣时长上限 dayMaxMoney:每日抵扣金额上限  onceMaxMoney:单次停车抵扣金额上限
        const { onceMaxNum=0, dayMaxNum=0, onceMaxTime=0, dayMaxTime=0, dayMaxMoney=0, onceMaxMoney=0 } = configRule;
        const { inTime } = parkInfo
        let onceUse = { count: 0, time: 0, money: 0 };
        let dayUse = { count: 0, time: 0, money: 0 };
        // 单次可用额度，当天可用额度
        let onceNumCanUse = 0, dayNumCanUse = 0;
        let onceTimeCanUse = 0, onceMoneyCanUse = 0;
        let dayTimeCanUse = 0, dayMoneyCanUse = 0;
        // 判断单次所有抵扣使用情况
        if (onceMaxTime > 0 || onceMaxMoney > 0 || onceMaxNum > 0) {
            onceUse = await  this.getPaymentCount({ parkId, plateNo, inTime });
        }

        // 可用额度
        onceNumCanUse = onceMaxNum > 0 ? onceMaxNum - onceUse.count : Infinity
        onceTimeCanUse = onceMaxTime > 0 ? onceMaxTime - onceUse.time : Infinity;
        onceMoneyCanUse = onceMaxMoney > 0 ? onceMaxMoney - onceUse.money : Infinity;
        if (onceNumCanUse <= 0) {
            logger.info(`【全局限制】单次抵扣次数（${onceMaxNum}次）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };

        }
        if (onceTimeCanUse <= 0) {
            logger.info(`【全局限制】单次抵扣时间额度（${onceMaxTime/60}分钟）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (onceMoneyCanUse <= 0) {

            logger.info(`【全局限制】单次抵扣金额(${(onceMaxMoney)/100}元）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }

        // 判断当日抵扣使用情况
        if (dayMaxTime > 0 || dayMaxMoney > 0 || dayMaxNum > 0) {
            let startTime = `${dayjs().format('YYYY-MM-DD')} 00:00:00`;
            let endTime = `${dayjs().format('YYYY-MM-DD')} 23:59:59`;
            dayUse = await this.getPaymentCount({ parkId, plateNo, startTime, endTime });
        }

        // 可用额度
        dayNumCanUse = dayMaxNum > 0 ? dayMaxNum - dayUse.count : Infinity
        dayTimeCanUse = dayMaxTime > 0 ? dayMaxTime - dayUse.time : Infinity;
        dayMoneyCanUse = dayMaxMoney > 0 ? dayMaxMoney - dayUse.money : Infinity;


        if (dayNumCanUse <= 0) {
            logger.info(`【全局限制】当日抵扣次数（${dayMaxNum}次）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (dayTimeCanUse <= 0) {
            logger.info(`【全局限制】当日抵扣时间额度（${dayMaxTime/60}分钟）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (dayMoneyCanUse <= 0) {
            logger.info(`【全局限制】当日抵扣额度（${dayMaxMoney/100}元）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        const timeCanUse = onceTimeCanUse - dayTimeCanUse < 0 ? onceTimeCanUse : dayTimeCanUse;
        const moneyCanUse = onceMoneyCanUse - dayMoneyCanUse < 0 ? onceMoneyCanUse : dayMoneyCanUse;
        return { timeCanUse, moneyCanUse }


    }




    /**
     * 获取使用规则的限制情况
     * @param {Object} params 
     * @param {Object} pileRule 使用规则限制
     * @param {Object} parkInfo 停车信息
     * 
     * @return {Int} canUse  可以使用额度 Infinity：表示不限额度
     */
    async getPileRuleLimit(params, pileRule, parkInfo) {
        if(!params.pileId){
            logger.info(`【使用规则限制】未获取到充电桩pileId`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        const { parkId, plateNo ,pileId} = params;
        // onceMaxNum: 单次抵扣次数上限 dayMaxNum： 每日抵扣次数上限 onceMaxTime:单次停车抵扣时长上限  dayMaxTime:每日抵扣时长上限 dayMaxMoney:每日抵扣金额上限  onceMaxMoney:单次停车抵扣金额上限
        const { onceMaxNum, dayMaxNum, onceMaxTime, dayMaxTime, dayMaxMoney, onceMaxMoney } = pileRule;
        const { inTime } = parkInfo
        let onceUse = { count: 0, time: 0, money: 0 };
        let dayUse = { count: 0, time: 0, money: 0 };
        // 单次可用额度，当天可用额度
        let onceNumCanUse = 0, dayNumCanUse = 0;
        let onceTimeCanUse = 0, onceMoneyCanUse = 0;
        let dayTimeCanUse = 0, dayMoneyCanUse = 0;
        // 判断单次所有抵扣使用情况
        if (onceMaxTime > 0 || onceMaxMoney > 0 || onceMaxNum > 0) {
            onceUse = await this.getPaymentCount({ parkId, plateNo, inTime,pileId });
        }

        // 可用额度
        onceNumCanUse = onceMaxNum > 0 ? onceMaxNum - onceUse.count : Infinity
        onceTimeCanUse = onceMaxTime > 0 ? onceMaxTime - onceUse.time : Infinity;
        onceMoneyCanUse = onceMaxMoney > 0 ? onceMaxMoney - onceUse.money : Infinity;
        if (onceNumCanUse <= 0) {
            logger.info(`【使用规则限制】单次抵扣次数（${onceMaxNum}次）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };

        }
        if (onceTimeCanUse <= 0) {
            logger.info(`【使用规则限制】单次抵扣时间额度（${onceMaxTime/60}分钟）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (onceMoneyCanUse <= 0) {

            logger.info(`【使用规则限制】单次会员抵扣金额(${(onceMaxMoney)/100}元）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }

        // 判断当日抵扣使用情况
        if (dayMaxTime > 0 || dayMaxMoney > 0 || dayMaxNum > 0) {
            let startTime = `${dayjs().format('YYYY-MM-DD')} 00:00:00`;
            let endTime = `${dayjs().format('YYYY-MM-DD')} 23:59:59`;
            dayUse = await this.getPaymentCount({ parkId, plateNo, startTime, endTime,pileId });
        }

        // 可用额度
        dayNumCanUse = dayMaxNum > 0 ? dayMaxNum - dayUse.count : Infinity
        dayTimeCanUse = dayMaxTime > 0 ? dayMaxTime - dayUse.time : Infinity;
        dayMoneyCanUse = dayMaxMoney > 0 ? dayMaxMoney - dayUse.money : Infinity;


        if (dayNumCanUse <= 0) {
            logger.info(`【使用规则限制】当日抵扣次数（${dayMaxNum}次）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (dayTimeCanUse <= 0) {
            logger.info(`【使用规则限制】当日抵扣时间额度（${dayMaxTime/60}分钟）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        if (dayMoneyCanUse <= 0) {
            logger.info(`【使用规则限制】当日抵扣额度（${dayMaxMoney/100}元）已达上限`);
            return { timeCanUse: 0, moneyCanUse: 0 };
        }
        const timeCanUse = onceTimeCanUse - dayTimeCanUse < 0 ? onceTimeCanUse : dayTimeCanUse;
        const moneyCanUse = onceMoneyCanUse - dayMoneyCanUse < 0 ? onceMoneyCanUse : dayMoneyCanUse;
        return { timeCanUse, moneyCanUse }


    }


     /**
     * 统计充电桩抵扣使用情况
     */
    async getPaymentCount(data) {
        const { parkId, plateNo = '', inTime = '', payMethod = '', startTime = '', endTime = '', pileId: paySource = '' } = data;
        // 收费终端为充电桩
        const payType = 1009;
        let count = 0, time = 0, money = 0;
        const options = {
            parkId: parkId,
            plateNo: plateNo,
            pageIndex: 1,
            pageSize: 100,
            inTime: inTime || undefined,
            startTime: startTime || undefined,
            endTime: endTime || undefined,
        }

        const axiosResponse = await axiosInstance.post<ApiResponse>('/api/wec/GetParkingPaymentList', options)
        throwIfResCodeNoSuccess(axiosResponse.data);
        const listRst = axiosResponse.data
         logger.info(JSON.stringify(listRst))
        if (listRst.resCode != '0'||JSON.parse(listRst?.data)?.plateList.length==0) {
           
            return { count, time, money };
        }

        for (let item of JSON.parse(listRst?.data)?.plateList) {
            if ((item.payMethod == payMethod || !payMethod) && item.payType == payType && (item.paySource == parseInt(paySource) || !paySource)) {
                count += 1;
                money +=(+item.freeMoney) ;
                time += (+item.freeTime);
            }
        }
       
       
        return { count, time, money };
    }


     

}

export { ChargingPileService }