package org.dromara.inspection.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.inspection.domain.ArExecution;
import org.dromara.inspection.domain.ArStep;
import org.dromara.inspection.domain.ArStepRecord;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.bo.ArStepRecordTreeBo;
import org.dromara.inspection.domain.vo.*;
import org.dromara.inspection.mapper.*;
import org.dromara.inspection.service.IArExecutionService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.AllArgsConstructor;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 任务执行记录Service业务层处理
 *
 * @author Lion Li
 * @date 2025-01-13
 */
@RequiredArgsConstructor
@Service
public class ArExecutionServiceImpl implements IArExecutionService {

    private final ArExecutionMapper baseMapper;
    private final ArTaskMapper taskMapper;
    private final ArRegionMapper regionMapper;
    private final ArDeviceMapper deviceMapper;
    private final ArStepMapper stepMapper;
    private final ArStepRecordMapper stepRecordMapper;

    @Override
    public ArExecutionVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public TableDataInfo<ArExecutionVo> queryPageList(ArExecutionBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ArExecution> lqw = buildQueryWrapper(bo);
        Page<ArExecutionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public List<ArExecutionVo> queryList(ArExecutionBo bo) {
        return baseMapper.selectVoList(buildQueryWrapper(bo));
    }

    private LambdaQueryWrapper<ArExecution> buildQueryWrapper(ArExecutionBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ArExecution> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getTaskId() != null, ArExecution::getTaskId, bo.getTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getExecutionCode()), ArExecution::getExecutionCode, bo.getExecutionCode());
        lqw.eq(bo.getRegionId() != null, ArExecution::getRegionId, bo.getRegionId());
        lqw.eq(bo.getDeviceId() != null, ArExecution::getDeviceId, bo.getDeviceId());
        lqw.eq(bo.getOperatorId() != null, ArExecution::getOperatorId, bo.getOperatorId());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), ArExecution::getStatus, bo.getStatus());
        lqw.between(params.get("beginStartTime") != null && params.get("endStartTime") != null,
            ArExecution::getStartTime, params.get("beginStartTime"), params.get("endStartTime"));
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            ArExecution::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.orderByDesc(ArExecution::getCreateTime);
        return lqw;
    }

    @Override
    public Boolean insertByBo(ArExecutionBo bo) {
        ArExecution add = MapstructUtils.convert(bo, ArExecution.class);
        validEntityBeforeSave(add);
        // 自动设置执行编号
        if (StringUtils.isBlank(add.getExecutionCode())) {
            add.setExecutionCode("EXE-" + System.currentTimeMillis());
        }
        // 设置初始状态
        if (StringUtils.isBlank(add.getStatus())) {
            add.setStatus("pending");
        }
        // 设置初始步骤数
        if (add.getTotalSteps() == null) {
            add.setTotalSteps(0);
        }
        if (add.getCompletedSteps() == null) {
            add.setCompletedSteps(0);
        }
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    @Override
    public Boolean updateByBo(ArExecutionBo bo) {
        ArExecution update = MapstructUtils.convert(bo, ArExecution.class);
        validEntityBeforeSave(update);
        // 如果状态变为 in_progress 且没有开始时间，自动设置
        if ("in_progress".equals(update.getStatus()) && update.getStartTime() == null) {
            update.setStartTime(new Date());
        }
        // 如果状态变为 completed 或 cancelled 且没有结束时间，自动设置
        if (("completed".equals(update.getStatus()) || "cancelled".equals(update.getStatus()))
            && update.getEndTime() == null) {
            update.setEndTime(new Date());
        }
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     *
     * @param entity 实体类数据
     */
    private void validEntityBeforeSave(ArExecution entity) {
        // 校验执行编号唯一性
        if (StringUtils.isNotBlank(entity.getExecutionCode())) {
            LambdaQueryWrapper<ArExecution> lqw = Wrappers.lambdaQuery();
            lqw.eq(ArExecution::getExecutionCode, entity.getExecutionCode());
            if (entity.getId() != null) {
                lqw.ne(ArExecution::getId, entity.getId());
            }
            long count = baseMapper.selectCount(lqw);
            if (count > 0) {
                throw new ServiceException("执行编号已存在!");
            }
        }
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // 校验是否有权限删除
            List<ArExecution> list = baseMapper.selectByIds(ids);
            if (list.size() != ids.size()) {
                throw new ServiceException("您没有删除权限!");
            }
            // TODO: 检查是否有关联的步骤记录，有则提示
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public ArExecutionDetailVo queryDetailById(Long id) {
        // 1. 查询执行记录
        ArExecutionVo execution = baseMapper.selectVoById(id);
        if (execution == null) {
            throw new ServiceException("任务执行记录不存在");
        }

        // 2. 查询关联对象
        ArTaskVo task = execution.getTaskId() != null
            ? taskMapper.selectVoById(execution.getTaskId()) : null;
        ArRegionVo region = execution.getRegionId() != null
            ? regionMapper.selectVoById(execution.getRegionId()) : null;
        ArDeviceVo device = execution.getDeviceId() != null
            ? deviceMapper.selectVoById(execution.getDeviceId()) : null;

        // 3. 查询步骤模板
        List<ArStepVo> allSteps = new ArrayList<>();
        if (execution.getTaskId() != null) {
            LambdaQueryWrapper<ArStep> stepWrapper = Wrappers.lambdaQuery();
            stepWrapper.eq(ArStep::getTaskId, execution.getTaskId());
            stepWrapper.orderByAsc(ArStep::getOrderNum);
            allSteps = stepMapper.selectVoList(stepWrapper);
        }

        // 4. 查询执行记录
        LambdaQueryWrapper<ArStepRecord> recordWrapper = Wrappers.lambdaQuery();
        recordWrapper.eq(ArStepRecord::getExecutionId, id);
        List<ArStepRecordVo> allRecords = stepRecordMapper.selectVoList(recordWrapper);

        // 5. 构建步骤记录 Map
        Map<Long, ArStepRecordVo> recordMap = allRecords.stream()
            .collect(Collectors.toMap(ArStepRecordVo::getStepId, r -> r, (r1, r2) -> r1));

        // 6. 构建步骤树
        List<ArStepRecordTreeVo> stepTree = buildStepRecordTree(allSteps, recordMap, 0L);

        // 7. 组装返回对象
        ArExecutionDetailVo detail = new ArExecutionDetailVo();
        detail.setExecution(execution);
        detail.setTask(task);
        detail.setRegion(region);
        detail.setDevice(device);
        detail.setStepTree(stepTree);

        return detail;
    }

    /**
     * 递归构建步骤执行记录树
     *
     * @param allSteps  所有步骤模板
     * @param recordMap 执行记录Map (stepId -> record)
     * @param parentId  父步骤ID
     * @return 树形步骤执行记录列表
     */
    private List<ArStepRecordTreeVo> buildStepRecordTree(
        List<ArStepVo> allSteps,
        Map<Long, ArStepRecordVo> recordMap,
        Long parentId) {

        List<ArStepRecordTreeVo> tree = new ArrayList<>();
        for (ArStepVo step : allSteps) {
            if (step.getParentId().equals(parentId)) {
                ArStepRecordTreeVo treeNode = new ArStepRecordTreeVo();

                // 复制步骤模板信息
                BeanUtils.copyProperties(step, treeNode);

                // 合并执行记录信息
                ArStepRecordVo record = recordMap.get(step.getId());
                if (record != null) {
                    treeNode.setRecordId(record.getId());
                    treeNode.setRecordStatus(record.getStatus());
                    treeNode.setIsDone(record.getIsDone());
                    treeNode.setStartTime(record.getStartTime());
                    treeNode.setCompletionTime(record.getCompletionTime());
                    treeNode.setDuration(record.getDuration());
                    treeNode.setTextFeedback(record.getTextFeedback());
                    treeNode.setVoiceText(record.getVoiceText());
                    treeNode.setAiResult(record.getAiResult());
                    treeNode.setExecutorId(record.getExecutorId());
                    treeNode.setExecutorName(record.getExecutorName());
                }

                // 递归查找子节点
                List<ArStepRecordTreeVo> children = buildStepRecordTree(allSteps, recordMap, step.getId());
                if (!children.isEmpty()) {
                    treeNode.setChildren(children);
                }

                tree.add(treeNode);
            }
        }
        return tree;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean submitExecution(ArExecutionSubmitBo bo) {
        // 1. 校验执行记录是否存在
        ArExecution execution = baseMapper.selectById(bo.getId());
        if (execution == null) {
            throw new ServiceException("任务执行记录不存在");
        }

        // 2. 收集所有需要更新的步骤记录
        List<ArStepRecord> recordsToUpdate = new ArrayList<>();
        if (bo.getStepTree() != null && !bo.getStepTree().isEmpty()) {
            collectRecordsToUpdate(bo.getStepTree(), bo.getId(), recordsToUpdate);
        }

        // 3. 批量更新步骤记录
        if (!recordsToUpdate.isEmpty()) {
            boolean updateSuccess = stepRecordMapper.updateBatchById(recordsToUpdate);
            if (!updateSuccess) {
                throw new ServiceException("步骤记录更新失败");
            }
        }

        // 4. 计算总步骤数和已完成步骤数
        StepCountResult countResult = countSteps(bo.getStepTree());

        // 5. 更新执行记录
        ArExecution updateExecution = new ArExecution();
        updateExecution.setId(bo.getId());

        // 设置状态
        if (StringUtils.isNotBlank(bo.getStatus())) {
            updateExecution.setStatus(bo.getStatus());
        }

        // 设置时间
        if (bo.getStartTime() != null) {
            updateExecution.setStartTime(bo.getStartTime());
        }
        if (bo.getEndTime() != null) {
            updateExecution.setEndTime(bo.getEndTime());
        }

        // 自动设置开始时间
        if ("in_progress".equals(updateExecution.getStatus()) && execution.getStartTime() == null) {
            updateExecution.setStartTime(new Date());
        }

        // 自动设置结束时间
        if (("completed".equals(updateExecution.getStatus()) || "cancelled".equals(updateExecution.getStatus()))
            && execution.getEndTime() == null) {
            updateExecution.setEndTime(new Date());
        }

        // 设置步骤统计
        updateExecution.setTotalSteps(countResult.getTotal());
        updateExecution.setCompletedSteps(countResult.getCompleted());

        return baseMapper.updateById(updateExecution) > 0;
    }

    /**
     * 递归收集所有需要更新的步骤记录
     *
     * @param stepTree    步骤树
     * @param executionId 执行ID
     * @param result      结果列表
     */
    private void collectRecordsToUpdate(List<ArStepRecordTreeBo> stepTree, Long executionId, List<ArStepRecord> result) {
        if (stepTree == null || stepTree.isEmpty()) {
            return;
        }

        for (ArStepRecordTreeBo stepBo : stepTree) {
            // 只更新有 recordId 的记录
            if (stepBo.getRecordId() != null) {
                ArStepRecord record = new ArStepRecord();
                record.setId(stepBo.getRecordId());
                record.setExecutionId(executionId);
                record.setStepId(stepBo.getId());
                record.setStatus(stepBo.getRecordStatus());
                record.setIsDone(stepBo.getIsDone());
                record.setStartTime(stepBo.getStartTime());
                record.setCompletionTime(stepBo.getCompletionTime());
                record.setTextFeedback(stepBo.getTextFeedback());
                record.setVoiceText(stepBo.getVoiceText());
                record.setAiResult(stepBo.getAiResult());
                record.setExecutorId(stepBo.getExecutorId());
                record.setExecutorName(stepBo.getExecutorName());

                // 自动计算耗时
                if (stepBo.getStartTime() != null && stepBo.getCompletionTime() != null) {
                    long durationMillis = stepBo.getCompletionTime().getTime() - stepBo.getStartTime().getTime();
                    record.setDuration((int) (durationMillis / 1000));
                } else if (stepBo.getDuration() != null) {
                    record.setDuration(stepBo.getDuration());
                }

                result.add(record);
            }

            // 递归处理子步骤
            if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
                collectRecordsToUpdate(stepBo.getChildren(), executionId, result);
            }
        }
    }

    /**
     * 递归统计步骤数
     *
     * @param stepTree 步骤树
     * @return 统计结果
     */
    private StepCountResult countSteps(List<ArStepRecordTreeBo> stepTree) {
        int totalCount = 0;
        int completedCount = 0;

        if (stepTree == null || stepTree.isEmpty()) {
            return new StepCountResult(totalCount, completedCount);
        }

        for (ArStepRecordTreeBo stepBo : stepTree) {
            // 只统计有 recordId 的步骤
            if (stepBo.getRecordId() != null) {
                totalCount++;
                if ("1".equals(stepBo.getIsDone()) || "completed".equals(stepBo.getRecordStatus())) {
                    completedCount++;
                }
            }

            // 递归统计子步骤
            if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
                StepCountResult childCounts = countSteps(stepBo.getChildren());
                totalCount += childCounts.getTotal();
                completedCount += childCounts.getCompleted();
            }
        }

        return new StepCountResult(totalCount, completedCount);
    }

    /**
     * 步骤统计结果内部类
     */
    @lombok.Data
    @AllArgsConstructor
    private static class StepCountResult {
        private int total;
        private int completed;
    }
}
