package com.ruoyi.iot.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.iot.constant.IotConstant;
import com.ruoyi.iot.domain.TDevicePlan;
import com.ruoyi.iot.domain.TPlanRecord;
import com.ruoyi.iot.domain.dto.TDevicePlanAddNewDTO;
import com.ruoyi.iot.entity.param.DeviceParam;
import com.ruoyi.iot.ex.ServiceException;
import com.ruoyi.iot.mapper.TDevicePlanMapper;
import com.ruoyi.iot.mapper.TDeviceRecordMapper;
import com.ruoyi.iot.service.DeviceControlerService;
import com.ruoyi.iot.service.ITDevicePlanService;
import com.ruoyi.iot.service.ITPlanRecordService;
import com.ruoyi.iot.transdata.RpcResult;
import com.ruoyi.iot.transdata.entity.PlanCharge;
import com.ruoyi.iot.util.DateUtil;
import com.ruoyi.iot.web.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class TDevicePlanServiceImpl extends ServiceImpl<TDevicePlanMapper, TDevicePlan> implements ITDevicePlanService {

    @Autowired
    TDevicePlanMapper tDevicePlanMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private DeviceControlerService deviceControlerService;

    @Autowired
    private ITPlanRecordService itPlanRecordService;

    @Autowired
    private TDeviceRecordMapper tDeviceRecordMapper;


    /**
     * 检查预约的单子
     *
     * @return
     */
    @Override
    public void checkReadyToRun() {
        LambdaQueryWrapper<TDevicePlan> tDevicePlanLambdaQueryWrapper = new LambdaQueryWrapper<>();
        tDevicePlanLambdaQueryWrapper.eq(TDevicePlan::getStatus ,1);
        tDevicePlanLambdaQueryWrapper.orderByDesc(TDevicePlan::getCreateTime);
        List<TDevicePlan> list = this.list(tDevicePlanLambdaQueryWrapper);
        LocalDateTime now = LocalDateTime.now();
//        now = now.plus(3, ChronoUnit.MINUTES);
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        DateTimeFormatter allFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        List<TPlanRecord> planList = new ArrayList<>();
        StringBuilder ids = new StringBuilder();
        for (TDevicePlan tDevicePlan : list) {
            Long id = tDevicePlan.getId();
            LocalDateTime createTime = tDevicePlan.getCreateTime();
            String deviceId = tDevicePlan.getDeviceId();
            Long planType = tDevicePlan.getPlanType();
            String ccid = tDevicePlan.getCcid();
            if(planType == null){
                planType = 0L;
            }
            DeviceParam deviceParam = new DeviceParam();
            deviceParam.setDeviceId(deviceId);
            deviceParam.setCcid(ccid);
            Long userId = tDevicePlan.getUserId();
            String redisKey = IotConstant.PLAN_CHARGE_DIR_REDIS_KEY+""+deviceId;
            if(planType.equals(1L)){//单次类型
                LocalDateTime runTime = tDevicePlan.getRunTime();
                int result = now.compareTo(runTime);
                if(result>=0){//代表需要执行时间了
                    if(ids.length()>0){
                        ids.append(",");
                    }
                    ids.append(id);
                    TPlanRecord tPlanRecord = new TPlanRecord();
                    tPlanRecord.setDeviceId(deviceId);
                    tPlanRecord.setCreateTime(LocalDateTime.now());
                    tPlanRecord.setUserId(userId);
                    tPlanRecord.setStatus(0);
                    tPlanRecord.setPlanId(id);
                    tPlanRecord.setCcid(ccid);
                    tPlanRecord.setPort(tDevicePlan.getPort());
                    planList.add(tPlanRecord);
                    deviceControlerService.sendPortDetailCmd(deviceParam);
                    redisCache.setCacheObject(redisKey,tDevicePlan);
                    redisCache.expire(redisKey,30, TimeUnit.SECONDS);
                    log.info("添加定时执行任务,{},{}",tDevicePlan.getId(),tDevicePlan.getDeviceId()+","+tDevicePlan.getRunTime());
                }
            }else{
                String repeatDays = tDevicePlan.getRepeatDays();
                String repeatTime = tDevicePlan.getRepeatTime();
                String[] days = repeatDays.split(",");
                int dayOfWeek = DateUtil.getDayOfWeek(now);
                boolean contain = Arrays.stream(days).anyMatch(e -> e.equals(dayOfWeek+""));
                if(contain){
                    repeatTime = repeatTime+":00";
                    String format = now.format(fmt);
                    String runTimeStr = format+" "+repeatTime;
                    LocalDateTime runTime = LocalDateTime.parse(runTimeStr,allFmt);
                    int result = now.compareTo(runTime);
                    if(result>0){
                        if(runTime.compareTo(createTime)<0){
                            log.info("该预约任务下次再执行,{},{}",tDevicePlan.getId(),tDevicePlan.getDeviceId());
                            continue;
                        }
                        boolean hasRun = checkHasRun(tDevicePlan, now);
                        if(hasRun){
                            log.info("该预约任务今日已经执行,{},{}",tDevicePlan.getId(),tDevicePlan.getDeviceId());
                            continue;
                        }
                        TPlanRecord tPlanRecord = new TPlanRecord();
                        tPlanRecord.setDeviceId(deviceId);
                        tPlanRecord.setCreateTime(LocalDateTime.now());
                        tPlanRecord.setUserId(userId);
                        tPlanRecord.setStatus(0);
                        tPlanRecord.setPlanId(id);
                        tPlanRecord.setCcid(ccid);
                        tPlanRecord.setPort(tDevicePlan.getPort());
                        planList.add(tPlanRecord);
                        deviceControlerService.sendPortDetailCmd(deviceParam);
                        redisCache.setCacheObject(redisKey,tDevicePlan);
                        redisCache.expire(redisKey,30, TimeUnit.SECONDS);
                        log.info("添加每日定时任务,{},{}",tDevicePlan.getId(),tDevicePlan.getDeviceId()+","+tDevicePlan.getRepeatTime());
                    }
                }
            }

        }

        if(ids.length()>0){
            tDevicePlanMapper.updatePlanById(ids.toString());
        }
        itPlanRecordService.bactchInsert(planList);
    }

    private boolean checkHasRun(TDevicePlan tDevicePlan,LocalDateTime now) {
        LambdaQueryWrapper<TPlanRecord> queryWrapper = Wrappers.lambdaQuery();
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String todayStr = now.format(fmt);
        queryWrapper.orderByDesc(TPlanRecord::getCreateTime);
        queryWrapper.apply("date_format(create_time,'%Y-%m-%d') = '"+todayStr+"'");
        queryWrapper.eq(TPlanRecord::getPlanId, tDevicePlan.getId());
        queryWrapper.last(" limit 1");
        TPlanRecord one = itPlanRecordService.getOne(queryWrapper);
        if (one != null) {
            LocalDateTime createTime = one.getCreateTime();


            String runTime = createTime.format(fmt);
            if (StringUtils.equals(runTime, todayStr)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计划任务每一分钟执行一次，检测预约充电是否成功
     * 若未成功，可以在手动充电一次
     */
    @Override
    public void checkRunSuccess() {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<TPlanRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.le(TPlanRecord::getCheckTime,now)
                .eq(TPlanRecord::getStatus,1);
        List<TPlanRecord> list = itPlanRecordService.list(lambdaQueryWrapper);
        for (TPlanRecord planRecord : list) {
            String ccid = planRecord.getCcid();
            String deviceId = planRecord.getDeviceId();
            planRecord.setStatus(2);
            String redisKey = IotConstant.START_CHARGE_DIR_REDIS+""+deviceId;
            redisCache.setCacheObject(redisKey,planRecord);
            redisCache.expire(redisKey,30, TimeUnit.SECONDS);
            DeviceParam deviceParam = new DeviceParam();
            deviceParam.setDeviceId(deviceId);
            deviceParam.setCcid(ccid);
            deviceControlerService.sendPortDetailCmd(deviceParam);
        }
        itPlanRecordService.saveOrUpdateBatch(list);
    }

    @Override
    public RpcResult cancelPlan(Long planId) {
        RpcResult rpcResult = null;
        LambdaQueryWrapper<TDevicePlan> objectLambdaQueryWrapper = Wrappers.lambdaQuery();
        objectLambdaQueryWrapper.eq(TDevicePlan::getId,planId);
        TDevicePlan tDevicePlan = this.getOne(objectLambdaQueryWrapper);
        if(tDevicePlan != null){
            Long status = tDevicePlan.getStatus();
            if(status.equals(3L)){
                rpcResult =RpcResult.fail("该预约记录已经取消");
            }else{
                tDevicePlan.setStatus(3L);
                this.updateById(tDevicePlan);
                log.info("用户取消预约,{},用户ID：{}",tDevicePlan.getId(),tDevicePlan.getUserId());
                String deviceId = tDevicePlan.getDeviceId();
                String ccid = tDevicePlan.getCcid();
                DeviceParam deviceParam = new DeviceParam();
                deviceParam.setDeviceId(deviceId);
                deviceParam.setCcid(ccid);
                String redisKey = IotConstant.CANCEL_CHARGE_DIR_REDIS+""+deviceId;
                deviceControlerService.sendPortDetailCmd(deviceParam);
                redisCache.setCacheObject(redisKey,tDevicePlan);
                redisCache.expire(redisKey,30, TimeUnit.SECONDS);
                log.info("添加取消用户预约检测,{},{}",tDevicePlan.getId(),tDevicePlan.getDeviceId()+","+tDevicePlan.getRepeatTime());
                rpcResult = RpcResult.ok();
            }

        }else{
            rpcResult =RpcResult.fail("找不到预约记录");
        }
        return rpcResult;
    }

    /**
     * 过4分钟的检查任务
     * @param deviceId
     * @param object
     */
    @Override
    public void runPlanChargeJob(String deviceId, JSONObject object) {
        String cancelKey = IotConstant.CANCEL_CHARGE_DIR_REDIS+""+deviceId;
        TDevicePlan tDevicePlan = redisCache.getAndDel(cancelKey);
        if(tDevicePlan !=null){//如果有取消的任务
            redisCache.deleteObject(cancelKey);
            cancelPlanCharge(deviceId,object,tDevicePlan);
            return;
        }
        String redisKey = IotConstant.PLAN_CHARGE_DIR_REDIS_KEY+""+deviceId;
        tDevicePlan = redisCache.getAndDel(redisKey);
        LocalDateTime planTime = LocalDateTime.now();
        if(tDevicePlan != null){//代表预约了任务，需要判断端口状态
            redisCache.deleteObject(redisKey);
            startPlanCharge(deviceId, object, tDevicePlan, planTime);
        }else{
            redisKey = IotConstant.START_CHARGE_DIR_REDIS+""+deviceId;
            TPlanRecord planRecord = redisCache.getAndDel(redisKey);
            if(planRecord != null){
                redisCache.deleteObject(redisKey);
                startCharge(deviceId, object, planRecord);
            }
        }


    }

    @Override
    public TDevicePlan getPlanInfo(String deviceId, String port, Long userId) {
        LambdaQueryWrapper<TDevicePlan> objectLambdaQueryWrapper = Wrappers.lambdaQuery();
        objectLambdaQueryWrapper.eq(TDevicePlan::getUserId,userId).eq(TDevicePlan::getStatus,1)
                .eq(TDevicePlan::getDeviceId,deviceId).eq(TDevicePlan::getPort,port).orderByDesc(TDevicePlan::getCreateTime)
                .last("limit 1");
        TDevicePlan one = this.getOne(objectLambdaQueryWrapper);
        return one;
    }

    @Override
    public void addNew(TDevicePlanAddNewDTO tDevicePlanAddNewDTO) {
        log.info("开始处理【添加预约】的业务，参数：{}", tDevicePlanAddNewDTO);
        // 创建TDevicePlan对象
        TDevicePlan tDevicePlan = new TDevicePlan();
        // 调用BeanUtils.copyProperties(源,目标)将参数对象中的属性复制到TDevicePlan对象中
        BeanUtils.copyProperties(tDevicePlanAddNewDTO, tDevicePlan);
        // 调用Mapper对象的insert()执行插入预约数据
        log.info("即将执行插入数据，参数：{}", tDevicePlan);
        tDevicePlan.setStatus(1L);
        boolean save = this.save(tDevicePlan);
        if (!save) {
            String message = "添加预约失败，服务器忙，请稍后再次尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }



    private void cancelPlanCharge(String deviceId, JSONObject object, TDevicePlan tDevicePlan) {
        Integer port = tDevicePlan.getPort();
        String ccid = tDevicePlan.getCcid();
        Integer port_status = null;
        if(port == 1){
            port_status = object.getInteger("port_first_status");
        }else if(port == 2){
            port_status = object.getInteger("port_second_status");
        }
        if(port_status == null){
            port_status = 0;
        }
        if(port_status == 6){
            deviceControlerService.cancelChargePlan(deviceId,ccid,port);
            log.info("端口{}已经预约，发送取消预约指令，{}",port,deviceId);
        }
    }

    /**
     * 手动开始充电
     * @param deviceId
     * @param object
     * @param planRecord
     */
    private void startCharge(String deviceId, JSONObject object, TPlanRecord planRecord) {
        Long planId = planRecord.getPlanId();
        LambdaQueryWrapper<TDevicePlan> objectLambdaQueryWrapper = Wrappers.lambdaQuery();
        objectLambdaQueryWrapper.eq(TDevicePlan::getId,planId);
        Integer port = planRecord.getPort();
        TDevicePlan one = this.getOne(objectLambdaQueryWrapper);
        if(one == null){
            planRecord.setStatus(-4);
            log.info("检测端口：{}预约任务未找到,无法执行{}",port, deviceId);
        }else{
            Long status = one.getStatus();
            if(status.equals(3L)){//代表已经取消
                planRecord.setStatus(-3);
                log.info("检测端口：{}预约任务已取消,未执行{}",port, deviceId);
            }else{
                Integer port_status = null;
                if(port == 1){
                    port_status = object.getInteger("port_first_status");
                }else if(port == 2){
                    port_status = object.getInteger("port_second_status");
                }
                if(port_status == null){
                    port_status = 0;
                }
                if(port_status != 2){
                    String ccid = planRecord.getCcid();
                    String deviceId1 = planRecord.getDeviceId();
                    planRecord.setStatus(-2);
                    deviceControlerService.startCharge(deviceId,ccid,port);
                    log.info("检测端口：{}未在充电--发送充电指令,{}",port, deviceId);
                }else{
                    planRecord.setStatus(3);
                    log.info("检测端口：{}自动充电--已经充电中,{}",port, deviceId);
                }
            }
        }
        itPlanRecordService.saveOrUpdate(planRecord);


    }

    /**
     * 开始计划充电
     * @param deviceId
     * @param object
     * @param tDevicePlan
     * @param planTime
     */
    private void startPlanCharge(String deviceId, JSONObject object, TDevicePlan tDevicePlan, LocalDateTime planTime) {
        LocalDateTime plus = planTime.plus(4, ChronoUnit.MINUTES);
        Long id = tDevicePlan.getId();
        Integer port = tDevicePlan.getPort();
        LambdaQueryWrapper<TDevicePlan> objectLambdaQueryWrapper = Wrappers.lambdaQuery();
        Long planId = tDevicePlan.getId();
        objectLambdaQueryWrapper.eq(TDevicePlan::getId,planId);
        TDevicePlan one = this.getOne(objectLambdaQueryWrapper);
        if(one == null){
            return;
        }
        Long status = one.getStatus();
        if(status.equals(3L)){
            log.info("检测端口：{}，预约任务已取消,未执行{}",port, deviceId);
            return;
        }
        String ccid = tDevicePlan.getCcid();
        Integer port_status = null;
        if(port == 1){
            port_status = object.getInteger("port_first_status");
        }else if(port == 2){
            port_status = object.getInteger("port_second_status");
        }
        if(port_status == null){
            port_status = 0;
        }
        if(port_status == 6){//代表端口已经预约，忽略本次
            log.info("端口{}已经预约，忽略本次预约，{}",port,deviceId);
            return;
        }
        LambdaQueryWrapper<TPlanRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(TPlanRecord::getPlanId, id).orderByDesc(TPlanRecord::getCreateTime).last("limit 1");
        TPlanRecord planRecord = itPlanRecordService.getOne(lambdaQueryWrapper);
        if(port_status == 5){//已经连接
            PlanCharge planCharge = new PlanCharge();
            planCharge.setMoney(50000);
            planCharge.setPort(port);
            planCharge.setTime(3);
            deviceControlerService.planCharge(deviceId,ccid,planCharge);

            planRecord.setStatus(1);
            planRecord.setPlanTime(planTime);
            planRecord.setCheckTime(plus);
            itPlanRecordService.saveOrUpdate(planRecord);
            log.info("端口{}已经连接，发送预约充电指令，{}",port,deviceId);
        }else if(port_status != 6){//
            planRecord.setStatus(-1);
            planRecord.setCheckResult("端口状态未连接,无法充电");
            itPlanRecordService.saveOrUpdate(planRecord);
            log.info("端口{}未连接，无法发送完成预约充电，{}",port,deviceId);
        }
    }


}
