package com.fizz.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fizz.business.domain.BizSendJob;
import com.fizz.business.domain.BizSendJobGroup;
import com.fizz.business.domain.BizSendJobItem;
import com.fizz.business.domain.dto.SendJobCreateDTO;
import com.fizz.business.domain.dto.SendJobQueryDTO;
import com.fizz.business.domain.vo.SendJobDetailVO;
import com.fizz.business.domain.vo.SendJobGroupVO;
import com.fizz.business.domain.vo.SendJobItemVO;
import com.fizz.business.mapper.BizSendJobGroupMapper;
import com.fizz.business.mapper.BizSendJobItemMapper;
import com.fizz.business.mapper.BizSendJobMapper;
import com.fizz.business.domain.CrmPdiPlan;
import com.fizz.business.domain.vo.FurnaceSendCoilInfoVO;
import com.fizz.business.service.CrmPdiPlanService;
import com.fizz.business.service.ISendJobService;
import com.fizz.business.service.manager.OpcMessageIdsManager;
import com.fizz.business.utils.RedisUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SendJobServiceImpl extends ServiceImpl<BizSendJobMapper, BizSendJob> implements ISendJobService {

    @Autowired
    private com.fizz.business.comm.OPC.OpcMessageSend opcMessageSend;


    @Autowired
    private BizSendJobGroupMapper jobGroupMapper;

    @Autowired
    private BizSendJobItemMapper jobItemMapper;

    @Autowired
    private OpcMessageIdsManager opcMessageIdsManager;

    @Autowired
    private CrmPdiPlanService crmPdiPlanService;

    private static final String REDIS_KEY_FURNACE_SEND_COIL_INFO = "furnace:send:coil:info";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer createSendJob(SendJobCreateDTO dto) {
        // 1. 创建批次
        BizSendJob job = new BizSendJob();
        BeanUtils.copyProperties(dto, job);

        // 生成业务唯一键
        // 生成业务唯一键（OpcMessageIdsManager 目前没有 generateMessageId 方法，改用 UUID）
        String bizKey = "SEND_JOB_" + UUID.randomUUID();
        job.setBizKey(bizKey);
        job.setStatus("PENDING");

        // 设置操作人
        String username = SecurityUtils.getUsername();
        job.setCreateBy(username);
        job.setCreateTime(new Date());

        // 保存批次
        baseMapper.insert(job);

        // 2. 保存分组与明细
        if (dto.getGroups() != null) {
            for (SendJobCreateDTO.GroupDTO groupDTO : dto.getGroups()) {
                // 2.1 保存分组
                BizSendJobGroup group = new BizSendJobGroup();
                BeanUtils.copyProperties(groupDTO, group);
                group.setJobId(job.getJobId());
                group.setStatus("PENDING");
                group.setCreateBy(username);
                group.setCreateTime(new Date());
                jobGroupMapper.insert(group);

                // 2.2 保存明细项
                if (groupDTO.getItems() != null) {
                    for (SendJobCreateDTO.ItemDTO itemDTO : groupDTO.getItems()) {
                        BizSendJobItem item = new BizSendJobItem();
                        BeanUtils.copyProperties(itemDTO, item);
                        item.setJobId(job.getJobId());
                        item.setGroupId(group.getGroupId());
                        item.setResultStatus("PENDING");
                        item.setCreateBy(username);
                        item.setCreateTime(new Date());

                        // 尝试将valueRaw转为数值
                        try {
                            item.setValueNum(new BigDecimal(itemDTO.getValueRaw()));
                        } catch (Exception e) {
                            log.warn("转换数值失败: {}", itemDTO.getValueRaw(), e);
                        }

                        jobItemMapper.insert(item);
                    }
                }
            }
        }

        return job.getJobId();
    }

    @Override
    public List<BizSendJob> selectSendJobList(SendJobQueryDTO query) {
        LambdaQueryWrapper<BizSendJob> qw = new LambdaQueryWrapper<>();
        qw.eq(StringUtils.isNotBlank(query.getDeviceName()),
                        BizSendJob::getDeviceName, query.getDeviceName())
                .eq(StringUtils.isNotBlank(query.getStatus()),
                        BizSendJob::getStatus, query.getStatus())
                .orderByDesc(BizSendJob::getCreateTime);

        List<BizSendJob> jobs = baseMapper.selectList(qw);
        if (jobs == null || jobs.isEmpty()) {
            return jobs;
        }

        // 如果传了 groupType（如 FURNACE），则仅保留包含该 groupType 的 job
        if (StringUtils.isNotBlank(query.getGroupType())) {
            String gt = query.getGroupType().trim();

            List<Integer> jobIds = jobs.stream().map(BizSendJob::getJobId).collect(Collectors.toList());
            List<BizSendJobGroup> groups = jobGroupMapper.selectList(
                    new LambdaQueryWrapper<BizSendJobGroup>()
                            .in(BizSendJobGroup::getJobId, jobIds)
                            .eq(BizSendJobGroup::getGroupType, gt)
            );
            if (groups == null || groups.isEmpty()) {
                return Collections.emptyList();
            }

            Set<Integer> allowedJobIds = groups.stream().map(BizSendJobGroup::getJobId).collect(Collectors.toSet());
            return jobs.stream().filter(j -> allowedJobIds.contains(j.getJobId())).collect(Collectors.toList());
        }

        return jobs;
    }

    @Override
    public SendJobDetailVO selectSendJobDetail(Integer jobId) {
        // 1. 查询任务
        BizSendJob job = baseMapper.selectById(jobId);
        if (job == null) {
            return null;
        }

        // 2. 转换为VO
        SendJobDetailVO detailVO = new SendJobDetailVO();
        BeanUtils.copyProperties(job, detailVO);

        // 3. 查询分组
        List<BizSendJobGroup> groups = jobGroupMapper.selectList(
                new LambdaQueryWrapper<BizSendJobGroup>()
                        .eq(BizSendJobGroup::getJobId, jobId)
                        .orderByAsc(BizSendJobGroup::getGroupNo)
        );

        if (groups.isEmpty()) {
            return detailVO;
        }

        // 4. 查询所有明细项
        List<Integer> groupIds = groups.stream()
                .map(BizSendJobGroup::getGroupId)
                .collect(Collectors.toList());

        List<BizSendJobItem> items = jobItemMapper.selectList(
                new LambdaQueryWrapper<BizSendJobItem>()
                        .in(BizSendJobItem::getGroupId, groupIds)
        );

        // 5. 按groupId分组
        Map<Integer, List<BizSendJobItem>> groupItemsMap = items.stream()
                .collect(Collectors.groupingBy(BizSendJobItem::getGroupId));

        // 6. 构建分组VO
        List<SendJobGroupVO> groupVOs = groups.stream().map(group -> {
            SendJobGroupVO groupVO = new SendJobGroupVO();
            BeanUtils.copyProperties(group, groupVO);

            // 设置分组下的明细项
            List<BizSendJobItem> groupItems = groupItemsMap
                    .getOrDefault(group.getGroupId(), Collections.emptyList());

            List<SendJobItemVO> itemVOs = groupItems.stream().map(item -> {
                SendJobItemVO itemVO = new SendJobItemVO();
                BeanUtils.copyProperties(item, itemVO);
                return itemVO;
            }).collect(Collectors.toList());

            groupVO.setItems(itemVOs);
            return groupVO;
        }).collect(Collectors.toList());

        detailVO.setGroups(groupVOs);
        return detailVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteSendJobByJobIds(Integer[] jobIds) {
        if (jobIds == null || jobIds.length == 0) {
            return true;
        }

        // 1. 更新任务状态为已删除
        BizSendJob updateJob = new BizSendJob();
        updateJob.setStatus("DELETED");
        updateJob.setUpdateBy(SecurityUtils.getUsername());
        updateJob.setUpdateTime(new Date());

        LambdaQueryWrapper<BizSendJob> qw = new LambdaQueryWrapper<>();
        qw.in(BizSendJob::getJobId, Arrays.asList(jobIds));

        return update(updateJob, qw);
    }

    /**
     * 执行发送：写入 OPC，并将发送结果保存为历史（更新 job/group/item 状态）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean executeSendJob(Integer jobId) {
        BizSendJob job = baseMapper.selectById(jobId);
        if (job == null) {
            return false;
        }
        if ("DELETED".equalsIgnoreCase(job.getStatus())) {
            return false;
        }

        // 更新 job 状态为发送中
        BizSendJob jobUpd = new BizSendJob();
        jobUpd.setJobId(jobId);
        jobUpd.setStatus("IN_PROGRESS");
        jobUpd.setActualSendTime(new Date());
        jobUpd.setUpdateBy(SecurityUtils.getUsername());
        jobUpd.setUpdateTime(new Date());
        baseMapper.updateById(jobUpd);

        // 查询该job下所有 PENDING 的 item
        List<BizSendJobItem> items = jobItemMapper.selectList(
                new LambdaQueryWrapper<BizSendJobItem>()
                        .eq(BizSendJobItem::getJobId, jobId)
                        .eq(BizSendJobItem::getResultStatus, "PENDING")
        );

        // 如果没有 PENDING 的项，检查是否有其他状态的项
        if (items == null || items.isEmpty()) {
            boolean hasItems = jobItemMapper.selectCount(
                    new LambdaQueryWrapper<BizSendJobItem>().eq(BizSendJobItem::getJobId, jobId)
            ) > 0;

            BizSendJob finish = new BizSendJob();
            finish.setJobId(jobId);
            finish.setStatus(hasItems ? "COMPLETED" : "FAILED");
            finish.setFinishTime(new Date());
            finish.setUpdateBy(SecurityUtils.getUsername());
            finish.setUpdateTime(new Date());
            finish.setRemark(hasItems ? "没有待发送的项" : "没有找到任何发送项");
            baseMapper.updateById(finish);
            return hasItems;
        }

        // 查询该job下group
        List<BizSendJobGroup> groups = jobGroupMapper.selectList(
                new LambdaQueryWrapper<BizSendJobGroup>().eq(BizSendJobGroup::getJobId, jobId)
        );
        for (BizSendJobGroup g : groups) {
            BizSendJobGroup gu = new BizSendJobGroup();
            gu.setGroupId(g.getGroupId());
            gu.setStatus("IN_PROGRESS");
            gu.setUpdateBy(SecurityUtils.getUsername());
            gu.setUpdateTime(new Date());
            jobGroupMapper.updateById(gu);
        }

        boolean allSuccess = true;

        // 按 address 分组，只发送最后一条记录
        Map<String, List<BizSendJobItem>> itemsByAddr = items.stream()
                .collect(Collectors.groupingBy(BizSendJobItem::getAddress));

        for (Map.Entry<String, List<BizSendJobItem>> entry : itemsByAddr.entrySet()) {
            List<BizSendJobItem> addrItems = entry.getValue();
            // 取最后一条（按 itemId 最大 || createTime 最新）
            addrItems.sort(Comparator.comparing(BizSendJobItem::getItemId));
            BizSendJobItem last = addrItems.get(addrItems.size() - 1);
            boolean success = true;
            String failMsg = null;
            try {
                // OpcMessageSend 没有 writeNode 方法，使用 writeData(identifier, value)
                // address 字段在本业务里就是 OPC 的 identifier（如：ns=2;s=...）
                opcMessageSend.writeData(last.getAddress(), last.getValueRaw());
            } catch (Exception ex) {
                success = false;
                allSuccess = false;
                failMsg = ex.getMessage();
            }

            // 更新该 address 下所有项的状态（统一）
            for (BizSendJobItem it : addrItems) {
                BizSendJobItem upd = new BizSendJobItem();
                upd.setItemId(it.getItemId());
                upd.setResultStatus(success ? "SUCCESS" : "FAILED");
                upd.setResultMsg(success ? "OK" : failMsg);
                upd.setUpdateBy(SecurityUtils.getUsername());
                upd.setUpdateTime(new Date());
                jobItemMapper.updateById(upd);
            }
        }

        // 更新 group 状态（按 group 下 item 是否全部成功）
        Map<Integer, List<BizSendJobItem>> itemByGroup = jobItemMapper.selectList(
                new LambdaQueryWrapper<BizSendJobItem>().eq(BizSendJobItem::getJobId, jobId)
        ).stream().collect(Collectors.groupingBy(BizSendJobItem::getGroupId));

        for (BizSendJobGroup g : groups) {
            List<BizSendJobItem> gi = itemByGroup.getOrDefault(g.getGroupId(), Collections.emptyList());
            boolean groupOk = gi.stream().allMatch(x -> "SUCCESS".equalsIgnoreCase(x.getResultStatus()));

            BizSendJobGroup gu = new BizSendJobGroup();
            gu.setGroupId(g.getGroupId());
            gu.setStatus(groupOk ? "COMPLETED" : "FAILED");
            gu.setUpdateBy(SecurityUtils.getUsername());
            gu.setUpdateTime(new Date());
            jobGroupMapper.updateById(gu);
        }

        // 更新 job 最终状态
        BizSendJob finish = new BizSendJob();
        finish.setJobId(jobId);
        finish.setFinishTime(new Date());
        finish.setUpdateBy(SecurityUtils.getUsername());
        finish.setUpdateTime(new Date());
        finish.setStatus(allSuccess ? "COMPLETED" : "PARTIAL_SUCCESS");
        finish.setRemark(allSuccess ? "全部发送成功" : "部分发送失败");
        baseMapper.updateById(finish);

        // 如果是炉火写入（FURNACE），获取当前正在线上的钢卷信息并存储到 Redis
        try {
            boolean isFurnaceSend = groups.stream()
                    .anyMatch(g -> "FURNACE".equalsIgnoreCase(g.getGroupType()));

            if (isFurnaceSend && allSuccess) {
                CrmPdiPlan currentCoil = crmPdiPlanService.getCurrentOnlineCoil();
                if (currentCoil != null) {
                    FurnaceSendCoilInfoVO coilInfo = new FurnaceSendCoilInfoVO();
                    coilInfo.setCoilId(currentCoil.getCoilid());
                    coilInfo.setPlanId(currentCoil.getPlanid());
                    coilInfo.setSendTime(new Date());

                    // 存储到 Redis（使用 JSON 格式）
                    ObjectMapper objectMapper = new ObjectMapper();
                    String jsonValue = objectMapper.writeValueAsString(coilInfo);
                    RedisUtil.setValue(REDIS_KEY_FURNACE_SEND_COIL_INFO, jsonValue);

                    log.info("炉火写入成功，已保存钢卷信息到 Redis: coilId={}, planId={}",
                            coilInfo.getCoilId(), coilInfo.getPlanId());
                } else {
                    log.warn("炉火写入成功，但未找到当前正在线上的钢卷");
                }
            }
        } catch (Exception e) {
            log.error("保存炉火写入钢卷信息到 Redis 失败", e);
            // 不影响主流程，只记录日志
        }

        return true;
    }
}

