package com.xy.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.collections.list.JArrayList;
import com.xy.collections.map.JMap;
import com.xy.consts.CommConsts;
import com.xy.dto.DeviceFaultInfoDto;
import com.xy.dto.DeviceFaultInfoFlowDto;
import com.xy.dto.SysCodeConfigureDto;
import com.xy.dto.be.MercDto;
import com.xy.entity.DeviceFaultInfo;
import com.xy.entity.DeviceFaultLog;
import com.xy.entity.DeviceInfo;
import com.xy.entity.SysCodeConfigureRedis;
import com.xy.error.CommRuntimeException;
import com.xy.event.DeviceEvent;
import com.xy.mapper.DeviceDataMapper;
import com.xy.mapper.DeviceFaultInfoMapper;
import com.xy.service.be.MercService;
import com.xy.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xy.utils.Beans.copy;
import static com.xy.utils.PlusBeans.toIPage;
import static com.xy.utils.PlusBeans.toPageBean;


/**
 * <p>
 * 设备故障信息 服务实现类
 * </p>
 *
 * @author 谭斌
 * @since 2024-01-08
 */
@Service
@RequiredArgsConstructor
@Api(tags = "设备故障信息")
public class DeviceFaultInfoServiceImpl extends ServiceImpl<DeviceFaultInfoMapper, DeviceFaultInfo> implements DeviceFaultInfoService {

    private final DeviceFaultLogServiceImpl deviceFaultLogService;
    private final MercService mercService;
    private final DeviceInfoServiceImpl deviceInfoService;
    private final DeviceFaultInfoFlowServiceImpl deviceFaultInfoFlowService;
    private final SysWorkUserService sysWorkUserService;
    private final DeviceDataMapper deviceDataMapper;
    private final SysCodeConfigureService sysCodeConfigureService;

    @PostMapping("countToDo")
    @ApiOperation("待处理设备故障统计")
    public R<Long> countToDo() {
        // 只统计严重 紧急
        long count = count(Wrappers.<DeviceFaultInfo>lambdaQuery().eq(DeviceFaultInfo::getState, 0).in(DeviceFaultInfo::getFaultLevel, CollUtil.newArrayList(3, 4)));
        return R.ok(count);
    }

    @PostMapping("countByDevice")
    @ApiOperation("按故障类型统计设备数")
    public R<List<DeviceFaultInfoDto.CountByDeviceVO>> countByDevice() {
        List<DeviceFaultInfoDto.CountByDeviceVO> list = countDeviceByType();
        return R.ok(list);
    }

    public List<DeviceFaultInfoDto.CountByDeviceVO> countDeviceByType() {
        List<DeviceFaultInfoDto.CountByDeviceVO> list = deviceDataMapper.deviceFaultInfoCount();
        if (CollUtil.isEmpty(list)) {
            return list;
        }
        list.forEach(d -> {
            SysCodeConfigureRedis s = SysCodeConfigureUtils.get(d.getEventCode());
            if (s != null) {
                d.setEventName(s.getTitle());
            }
        });

        return list;
    }

    @PostMapping("countByDeviceHomePage")
    @ApiOperation("按故障类型统计设备数-运营首页")
    public R<List<DeviceFaultInfoDto.CountByDeviceVO>> countByDeviceHomePage() {
        List<DeviceFaultInfoDto.CountByDeviceVO> list = countDeviceByType();
        if (CollUtil.isEmpty(list)) {
            return R.ok(list);
        }
        // 最多只取5个
        if (list.size() > 5) {
            list = CollUtil.sub(list, 0, 5);
        }
        return R.ok(list);
    }

    @PostMapping("obj")
    @ApiOperation("对象查询")
    public R<DeviceFaultInfoDto.Vo> obj(@RequestBody DeviceFaultInfoDto.Vo vo) {
        DeviceFaultInfoDto.SelectList selectList = copy(DeviceFaultInfoDto.SelectList.class, vo);
        List<DeviceFaultInfoDto.Vo> list = list(selectList).getData();
        if (Emptys.check(list)) {
            return R.ok(list.get(0));
        }
        return R.ok();
    }

    @PostMapping("list")
    @ApiOperation("集合查询")
    public R<List<DeviceFaultInfoDto.Vo>> list(@RequestBody DeviceFaultInfoDto.SelectList selectList) {
        LambdaQueryWrapper<DeviceFaultInfo> lambdaQueryWrapper = new MybatisPlusQuery().eqWrapper(selectList, DeviceFaultInfo.class).build();
        List<DeviceFaultInfo> list = list(lambdaQueryWrapper);
        return R.ok(copy(DeviceFaultInfoDto.Vo.class, list));
    }

    @PostMapping("page")
    @ApiOperation("分页查询")
    public R<PageBean<DeviceFaultInfoDto.Vo>> page(@RequestBody DeviceFaultInfoDto.Page page) {
        PageBean pageBean = page.getPage();
        LocalDateTime beginTime = page.getBeginCreateTime();
        LocalDateTime endTime = page.getEndCreateTime();
        LambdaQueryWrapper<DeviceFaultInfo> lambdaQueryWrapper = new MybatisPlusQuery()
                .eqWrapper(page, DeviceFaultInfo.class).build()
                .ge(beginTime != null, DeviceFaultInfo::getCreateTime, beginTime)
                .le(endTime != null, DeviceFaultInfo::getCreateTime, endTime);

        Long sysId = AuthorizeUtils.getSysId();
        List<String> roleCodes = AuthorizeUtils.getRoleCodes(sysId);
        // 售后角色只看转售后的，其他角色看所有
        if (roleCodes.contains(CommConsts.MERC_SH_ADMIN)) {
            lambdaQueryWrapper.eq(DeviceFaultInfo::getToAfterSale, true);
        }

        IPage<DeviceFaultInfo> iPage = page(toIPage(pageBean), lambdaQueryWrapper);
        PageBean<DeviceFaultInfoDto.Vo> data = toPageBean(DeviceFaultInfoDto.Vo.class, iPage);
        List<DeviceFaultInfoDto.Vo> records = data.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<Long> mercIds = records.stream().map(DeviceFaultInfoDto.Vo::getMercId).distinct().collect(Collectors.toList());
            List<String> codes = records.stream().map(DeviceFaultInfoDto.Vo::getEventCode).distinct().collect(Collectors.toList());
            SysCodeConfigureDto.SelectList selectList = new SysCodeConfigureDto.SelectList();
            selectList.setCodes(codes);
            List<SysCodeConfigureDto.Vo> vos = R.feignCheckData(sysCodeConfigureService.list(selectList));
            Map<String, SysCodeConfigureDto.Vo> sysCodeMap = MapUtil.newHashMap();
            if (CollUtil.isNotEmpty(vos)) {
                sysCodeMap = vos.stream().collect(Collectors.toMap(SysCodeConfigureDto.Vo::getCode, i -> i));
            }
            List<MercDto.Vo> mercs = mercService.list(new MercDto.SelectList().setMercIds(mercIds)).getData();
            JMap<Long, MercDto.Vo> longVoJMap = new JArrayList<>(mercs).toMap(MercDto.Vo::getId).cover();
            for (DeviceFaultInfoDto.Vo r : records) {
                Long mercId = r.getMercId();
                if (longVoJMap.get(mercId) != null) {
                    r.setMercName(longVoJMap.get(mercId).getName());
                }
                SysCodeConfigureDto.Vo sysCodeInfo = sysCodeMap.get(r.getEventCode());
                // 故障说明与建议
                if (sysCodeInfo != null) {
                    r.setMsg(sysCodeInfo.getMsg());
                    r.setPropose(sysCodeInfo.getPropose());
                }
            }

            data.setRecords(records);
        }

        return R.ok(data);
    }

    @PostMapping("save")
    @ApiOperation("添加")
    public R save(@RequestBody @Validated DeviceFaultInfoDto.Save save) {
        DeviceFaultInfo saveInfo = copy(DeviceFaultInfo.class, save);
        save(saveInfo);
        //发布事件
        DeviceEvent.FaultEvent faultEvent = new DeviceEvent.FaultEvent()
                .setFaultId(saveInfo.getFaultId())
                .setDescribes(saveInfo.getDescribes())
                .setEventNum(saveInfo.getEventNum())
                .setEventCode(saveInfo.getEventCode())
                .setState(false);
        faultEvent.setValue(saveInfo.getDeviceId());
        EventUtils.push(() -> faultEvent);
        return R.ok();
    }

    @PostMapping("update")
    @ApiOperation("修改")
    public R update(@RequestBody @Validated DeviceFaultInfoDto.Update update) {
        DeviceFaultInfo updateInfo = copy(DeviceFaultInfo.class, update);
        updateById(updateInfo);
        return R.ok();
    }

    @PostMapping("resolve")
    @ApiOperation("标记已解决")
    public R resolve(@RequestBody @Validated DeviceFaultInfoDto.Update update) {
        resolveDone(update);
        return R.ok();
    }

    @Transactional(rollbackFor = Exception.class)
    public void resolveDone(DeviceFaultInfoDto.Update update) {
        Long faultId = update.getFaultId();
        DeviceFaultInfo deviceFaultInfo = this.getById(faultId);
        if (deviceFaultInfo == null) {
            throw new CommRuntimeException("操作对象不存在！");
        }
        DeviceFaultInfo updateInfo = copy(DeviceFaultInfo.class, update);
        updateInfo.setState(true);
        updateInfo.setResolveTime(LocalDateTime.now());
        updateInfo.setFaultLevel(-1);
        boolean b = updateById(updateInfo);
        if (b) {
            DeviceFaultInfoFlowDto.Save save = new DeviceFaultInfoFlowDto.Save();
            save.setFaultId(faultId);
            save.setContent("故障已解决");
            deviceFaultInfoFlowService.save(save);
            // 清理日志 故障信息表标记已解决，清除日志表中对应设备事件在标记已解决时间之前所有事件。
            deviceFaultLogService.remove(Wrappers.<DeviceFaultLog>lambdaUpdate().le(DeviceFaultLog::getEventTime, LocalDateTime.now()));
            // 更新故障等级为正常
            deviceInfoService.updateById(new DeviceInfo().setDeviceId(deviceFaultInfo.getDeviceId()).setFaultLevel(-1));
            //发布事件
            DeviceEvent.FaultEvent faultEvent = new DeviceEvent.FaultEvent()
                    .setFaultId(deviceFaultInfo.getFaultId())
                    .setDescribes(deviceFaultInfo.getDescribes())
                    .setEventNum(deviceFaultInfo.getEventNum())
                    .setEventCode(deviceFaultInfo.getEventCode())
                    .setState(true);
            faultEvent.setValue(deviceFaultInfo.getDeviceId());
            EventUtils.push(() -> faultEvent);
        }
    }
}
