package com.ruoyi.iot.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.dto.TDevicePlanAddNewDTO;
import com.ruoyi.iot.domain.vo.TDevicePlanVO;
import com.ruoyi.iot.domain.TPlanRecord;
import com.ruoyi.iot.entity.param.DeviceParam;
import com.ruoyi.iot.ex.ServiceException;
import com.ruoyi.iot.mapper.TDevicePlanMapper;
import com.ruoyi.iot.service.DeviceControlerService;
import com.ruoyi.iot.service.ITDevicePlanService;
import com.ruoyi.iot.service.ITPlanRecordService;
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.stereotype.Service;

import java.time.LocalDateTime;
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;

    @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);
        int rows = tDevicePlanMapper.insert(tDevicePlan);
        if (rows != 1) {
            String message = "添加预约失败，服务器忙，请稍后再次尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }

    /**
     * 检查预约的单子
     *
     * @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);
        List<TPlanRecord> planList = new ArrayList<>();
        StringBuilder ids = new StringBuilder();
        for (TDevicePlan tDevicePlan : list) {
            Long id = tDevicePlan.getId();
            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);
                    planList.add(tPlanRecord);
                    deviceControlerService.sendPortDetailCmd(deviceParam);
                    redisCache.setCacheObject(redisKey,tDevicePlan);
                    redisCache.expire(redisKey,30, TimeUnit.SECONDS);
                }
            }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){
                    String hour = now.getHour()+"";
                    String minute = now.getMinute()+"";
                    if(minute.length() == 1){
                        minute = "0"+minute;
                    }
                    String curMin = hour+":"+minute;
                    if(curMin.equals(repeatTime)){
                        TPlanRecord tPlanRecord = new TPlanRecord();
                        tPlanRecord.setDeviceId(deviceId);
                        tPlanRecord.setCreateTime(LocalDateTime.now());
                        tPlanRecord.setUserId(userId);
                        tPlanRecord.setStatus(0);
                        tPlanRecord.setPlanId(id);
                        tPlanRecord.setCcid(ccid);
                        planList.add(tPlanRecord);
                        deviceControlerService.sendPortDetailCmd(deviceParam);
                        redisCache.setCacheObject(redisKey,tDevicePlan);
                        redisCache.expire(redisKey,30, TimeUnit.SECONDS);
                    }
                }
            }

        }

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

    @Override
    public List<TDevicePlanVO> getStandardByUserId(Long userId) {
        log.debug("开始处理【根据用戶id查询详情】的业务，参数：{}", userId);

        // 调用Mapper的countByDeviceId()执行查询
        List<TDevicePlanVO> tChargeRecordVO = tDevicePlanMapper.ListByUserId(userId);

        log.debug("即将返回查询结果：{}", tChargeRecordVO);
        return (List<TDevicePlanVO>) tChargeRecordVO;
    }

    /**
     * 计划任务每一分钟执行一次，检测预约充电是否成功
     * 若未成功，可以在手动充电一次
     */
    @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);
    }

    /**
     * 过4分钟的检查任务
     * @param deviceId
     * @param object
     */
    @Override
    public void runPlanChargeJob(String deviceId, JSONObject object) {
        String redisKey = IotConstant.PLAN_CHARGE_DIR_REDIS_KEY+""+deviceId;
        TDevicePlan 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);
            }
        }


    }

    /**
     * 手动开始充电
     * @param deviceId
     * @param object
     * @param planRecord
     */
    private void startCharge(String deviceId, JSONObject object, TPlanRecord planRecord) {
        Integer port_first_status = object.getInteger("port_first_status");
        if(port_first_status == null){
            port_first_status = 0;
        }
        if(port_first_status != 2){
            String ccid = planRecord.getCcid();
            String deviceId1 = planRecord.getDeviceId();
            planRecord.setStatus(-2);
            deviceControlerService.startCharge(deviceId,ccid);
            log.info("检测未在充电--发送充电指令,{}", deviceId);
        }else{
            planRecord.setStatus(3);
            log.info("检测自动充电--已经充电中,{}", 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();
        String ccid = tDevicePlan.getCcid();
        Integer port_first_status = object.getInteger("port_first_status");
        if(port_first_status == null){
            port_first_status = 0;
        }
        if(port_first_status == 6){//代表端口已经预约，忽略本次
            log.info("端口已经预约，忽略本次预约，{}",deviceId);
            return;
        }
        LambdaQueryWrapper<TPlanRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(TPlanRecord::getPlanId, id);
        TPlanRecord planRecord = itPlanRecordService.getOne(lambdaQueryWrapper);
        if(port_first_status == 5){//已经连接
            PlanCharge planCharge = new PlanCharge();
            planCharge.setMoney(50000);
            planCharge.setPort(1);
            planCharge.setTime(1);
            deviceControlerService.planCharge(deviceId,ccid,planCharge);

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


}
