package com.aegis.emergency.dispatch.service.impl;

import com.aegis.common.exception.ServiceException;
import com.aegis.common.utils.SecurityUtils;
import com.aegis.common.utils.StringUtils;
import com.aegis.common.utils.uuid.Seq;
import com.aegis.emergency.dispatch.constant.DispatchTaskStatus;
import com.aegis.emergency.dispatch.constant.DispatchTaskStepStatus;
import com.aegis.emergency.dispatch.domain.DispatchResourceUsageLink;
import com.aegis.emergency.dispatch.domain.DispatchTask;
import com.aegis.emergency.dispatch.domain.DispatchTaskParticipant;
import com.aegis.emergency.dispatch.domain.DispatchTaskStep;
import com.aegis.emergency.dispatch.mapper.DispatchTaskMapper;
import com.aegis.emergency.dispatch.service.IDispatchResourceUsageLinkService;
import com.aegis.emergency.dispatch.service.IDispatchTaskLogService;
import com.aegis.emergency.dispatch.service.IDispatchTaskParticipantService;
import com.aegis.emergency.dispatch.service.IDispatchTaskService;
import com.aegis.emergency.dispatch.service.IDispatchTaskStepService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 调度任务 Service 实现
 *
 * @author aegis
 */
@Service
public class DispatchTaskServiceImpl extends ServiceImpl<DispatchTaskMapper, DispatchTask>
        implements IDispatchTaskService {

    @Autowired
    private IDispatchTaskParticipantService participantService;

    @Autowired
    private IDispatchTaskStepService stepService;

    @Autowired
    private IDispatchResourceUsageLinkService resourceUsageLinkService;

    @Autowired
    private IDispatchTaskLogService logService;

    @Override
    public List<DispatchTask> listByCondition(DispatchTask condition) {
        LambdaQueryWrapper<DispatchTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DispatchTask::getDelFlag, "0");
        if (condition != null) {
            wrapper.eq(condition.getEventId() != null, DispatchTask::getEventId, condition.getEventId())
                .eq(StringUtils.isNotBlank(condition.getStatus()), DispatchTask::getStatus, condition.getStatus())
                .like(StringUtils.isNotBlank(condition.getName()), DispatchTask::getName, condition.getName())
                .between(condition.getBeginCreateTime() != null && condition.getEndCreateTime() != null,
                    DispatchTask::getCreateTime, condition.getBeginCreateTime(), condition.getEndCreateTime());
        }
        wrapper.orderByDesc(DispatchTask::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<DispatchTask> listMyTasks(DispatchTask condition, String keyword) {
        Long userId = SecurityUtils.getUserId();
        if (userId == null) {
            throw new ServiceException("未获取到当前用户信息");
        }

        String participantTaskQuery = "SELECT task_id FROM dispatch_task_participant "
            + "WHERE del_flag = '0' AND (person_id = " + userId
            + " OR person_id IN (SELECT id FROM resource_team_member WHERE user_id = " + userId + "))";

        LambdaQueryWrapper<DispatchTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DispatchTask::getDelFlag, "0")
            .and(w -> w.eq(DispatchTask::getAssigneeId, userId)
                .or().inSql(DispatchTask::getId, participantTaskQuery));

        if (condition != null) {
            wrapper.eq(condition.getEventId() != null, DispatchTask::getEventId, condition.getEventId())
                .eq(StringUtils.isNotBlank(condition.getStatus()), DispatchTask::getStatus, condition.getStatus())
                .between(condition.getBeginCreateTime() != null && condition.getEndCreateTime() != null,
                    DispatchTask::getCreateTime, condition.getBeginCreateTime(), condition.getEndCreateTime());

            if (StringUtils.isNotBlank(condition.getEventLevel())) {
                wrapper.apply("event_id IN (SELECT id FROM event WHERE level = {0})",
                    condition.getEventLevel());
            }

            if (Boolean.TRUE.equals(condition.getExcludeDraft())) {
                wrapper.ne(DispatchTask::getStatus, DispatchTaskStatus.DRAFT.getCode());
            }
        }

        if (StringUtils.isNotBlank(keyword)) {
            wrapper.and(w -> w.like(DispatchTask::getName, keyword)
                .or().like(DispatchTask::getTaskNo, keyword));
        }

        wrapper.orderByDesc(DispatchTask::getUpdateTime);
        return this.list(wrapper);
    }

    @Override
    public DispatchTask getByTaskNo(String taskNo) {
        if (StringUtils.isBlank(taskNo)) {
            return null;
        }
        return this.getOne(new LambdaQueryWrapper<DispatchTask>()
            .eq(DispatchTask::getTaskNo, taskNo)
            .eq(DispatchTask::getDelFlag, "0"));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean publish(Long id) {
        DispatchTask task = requireTask(id);
        ensureStatus(task, DispatchTaskStatus.DRAFT);
        task.setStatus(DispatchTaskStatus.PUBLISHED.getCode());
        applyAudit(task);
        boolean result = this.updateById(task);
        recordLog(task.getId(), "publish", "任务发布");
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markInProgress(Long id) {
        DispatchTask task = requireTask(id);
        ensureStatus(task, DispatchTaskStatus.PUBLISHED);
        task.setStatus(DispatchTaskStatus.IN_PROGRESS.getCode());
        applyAudit(task);
        boolean result = this.updateById(task);
        recordLog(task.getId(), "in_progress", "任务执行中");
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean close(Long id, String remark) {
        DispatchTask task = requireTask(id);
        ensureStatus(task, DispatchTaskStatus.IN_PROGRESS);
        ensureStepsCompleted(task.getId());
        releaseResources(task.getId());
        task.setStatus(DispatchTaskStatus.CLOSED.getCode());
        task.setRemark(remark);
        applyAudit(task);
        boolean result = this.updateById(task);
        recordLog(task.getId(), "close", remark);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean abort(Long id, String reason) {
        DispatchTask task = requireTask(id);
        if (task.getStatus().equals(DispatchTaskStatus.DRAFT.getCode())) {
            throw new ServiceException("草稿任务不能取消，请直接删除");
        }
        if (task.getStatus().equals(DispatchTaskStatus.CLOSED.getCode())) {
            throw new ServiceException("已关闭的任务不能取消");
        }
        if (task.getStatus().equals(DispatchTaskStatus.CANCELLED.getCode())) {
            throw new ServiceException("任务已经取消");
        }
        // Release resources for published or in-progress tasks
        // Resources can be allocated to both task-level and step-level, all will be released
        if (task.getStatus().equals(DispatchTaskStatus.PUBLISHED.getCode()) 
            || task.getStatus().equals(DispatchTaskStatus.IN_PROGRESS.getCode())) {
            releaseResources(task.getId());
        }
        task.setStatus(DispatchTaskStatus.CANCELLED.getCode());
        task.setRemark(reason);
        applyAudit(task);
        boolean result = this.updateById(task);
        recordLog(task.getId(), "abort", reason);
        return result;
    }

    @Override
    public List<DispatchTaskParticipant> listParticipants(Long taskId) {
        if (taskId == null) {
            throw new ServiceException("任务ID不能为空");
        }
        requireTask(taskId);
        return participantService.listByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addParticipants(Long taskId, List<DispatchTaskParticipant> participants) {
        requireTask(taskId);
        participantService.addParticipants(taskId, participants);
        recordLog(taskId, "add_participant", "新增参与方");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeParticipant(Long taskId, Long participantId) {
        requireTask(taskId);
        boolean removed = participantService.removeParticipant(taskId, participantId);
        if (removed) {
            recordLog(taskId, "remove_participant", "移除参与方");
        }
        return removed;
    }

    @Override
    public List<DispatchTaskStep> listSteps(Long taskId) {
        if (taskId == null) {
            throw new ServiceException("任务ID不能为空");
        }
        requireTask(taskId);
        return stepService.listByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upsertSteps(Long taskId, List<DispatchTaskStep> steps) {
        requireTask(taskId);
        
        if (steps == null || steps.isEmpty()) {
            return;
        }
        
        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        
        // Get existing steps for this task
        List<DispatchTaskStep> existingSteps = stepService.listByTaskId(taskId);
        Map<Long, DispatchTaskStep> existingMap = existingSteps.stream()
            .collect(Collectors.toMap(DispatchTaskStep::getId, s -> s));
        
        List<DispatchTaskStep> toInsert = new ArrayList<>();
        List<DispatchTaskStep> toUpdate = new ArrayList<>();
        
        for (DispatchTaskStep step : steps) {
            if (step == null) {
                continue;
            }
            
            step.setTaskId(taskId);
            if (step.getStepOrder() == null) {
                step.setStepOrder(0);
            }
            if (StringUtils.isEmpty(step.getStatus())) {
                step.setStatus(DispatchTaskStepStatus.PENDING.getCode());
            }
            step.setDelFlag("0");
            
            if (step.getId() != null && existingMap.containsKey(step.getId())) {
                // Update existing step
                DispatchTaskStep existing = existingMap.get(step.getId());
                step.setCreateBy(existing.getCreateBy());
                step.setCreateTime(existing.getCreateTime());
                step.setUpdateBy(operator);
                step.setUpdateTime(now);
                toUpdate.add(step);
            } else {
                // New step (no ID or ID doesn't exist)
                if (step.getId() != null) {
                    // Invalid ID, clear it
                    step.setId(null);
                }
                step.setCreateBy(operator);
                step.setCreateTime(now);
                step.setUpdateBy(operator);
                step.setUpdateTime(now);
                toInsert.add(step);
            }
        }
        
        // Perform batch operations
        if (!toInsert.isEmpty()) {
            stepService.saveBatch(toInsert);
        }
        if (!toUpdate.isEmpty()) {
            stepService.updateBatchById(toUpdate);
        }
        
        recordLog(taskId, "upsert_steps", "更新行动项");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean startStep(Long taskId, Long stepId) {
        requireTask(taskId);
        boolean result = stepService.startStep(taskId, stepId);
        if (result) {
            recordLog(taskId, "start_step", "开始执行行动项: " + stepId);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean completeStep(Long taskId, Long stepId, String remark) {
        requireTask(taskId);
        boolean result = stepService.completeStep(taskId, stepId, remark);
        if (result) {
            recordLog(taskId, "complete_step", "完成行动项: " + stepId + (StringUtils.isNotBlank(remark) ? ", 备注: " + remark : ""));
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean failStep(Long taskId, Long stepId, String reason) {
        requireTask(taskId);
        boolean result = stepService.failStep(taskId, stepId, reason);
        if (result) {
            recordLog(taskId, "fail_step", "标记行动项失败: " + stepId + (StringUtils.isNotBlank(reason) ? ", 原因: " + reason : ""));
        }
        return result;
    }

    @Override
    public List<DispatchResourceUsageLink> listResourceLinks(Long taskId) {
        if (taskId == null) {
            return Collections.emptyList();
        }
        return resourceUsageLinkService.listByTaskId(taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(DispatchTask task) {
        if (task == null) {
            throw new ServiceException("任务信息不能为空");
        }
        if (StringUtils.isBlank(task.getTaskNo())) {
            task.setTaskNo(generateTaskNo());
        }
        if (StringUtils.isBlank(task.getStatus())) {
            task.setStatus(DispatchTaskStatus.DRAFT.getCode());
        }
        task.setDelFlag("0");
        applyAudit(task, true);
        boolean saved = super.save(task);
        recordLog(task.getId(), "create", "创建任务");
        return saved;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(DispatchTask task) {
        if (task == null || task.getId() == null) {
            throw new ServiceException("任务ID不能为空");
        }
        DispatchTask existing = requireTask(task.getId());
        if (StringUtils.isNotBlank(task.getName())) {
            existing.setName(task.getName());
        }
        if (task.getDescription() != null) {
            existing.setDescription(task.getDescription());
        }
        if (task.getAssigneeId() != null) {
            existing.setAssigneeId(task.getAssigneeId());
        }
        if (task.getEventId() != null) {
            existing.setEventId(task.getEventId());
        }
        if (task.getPlanTemplateId() != null) {
            existing.setPlanTemplateId(task.getPlanTemplateId());
        }
        applyAudit(existing);
        boolean updated = super.updateById(existing);
        recordLog(existing.getId(), "update", "更新任务");
        return updated;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        if (id == null) {
            throw new ServiceException("任务ID不能为空");
        }
        Long taskId = (Long) id;
        DispatchTask task = requireTask(taskId);
        task.setDelFlag("2");
        applyAudit(task);
        boolean removed = super.updateById(task);
        recordLog(task.getId(), "delete", "删除任务");
        return removed;
    }

    private DispatchTask requireTask(Long id) {
        DispatchTask task = this.getById(id);
        if (task == null || !"0".equals(task.getDelFlag())) {
            throw new ServiceException("任务不存在");
        }
        return task;
    }

    private void ensureStatus(DispatchTask task, DispatchTaskStatus expected) {
        if (!expected.getCode().equals(task.getStatus())) {
            throw new ServiceException("任务状态必须为 " + expected.getLabel());
        }
    }

    private void ensureStepsCompleted(Long taskId) {
        List<DispatchTaskStep> steps = stepService.listByTaskId(taskId);
        boolean hasPending = steps.stream()
            .anyMatch(step -> !DispatchTaskStepStatus.COMPLETED.getCode().equals(step.getStatus()));
        if (hasPending) {
            throw new ServiceException("存在未完成的行动项，不能关闭任务");
        }
    }

    private void releaseResources(Long taskId) {
        List<DispatchResourceUsageLink> links = resourceUsageLinkService.listByTaskId(taskId);
        links.forEach(link -> resourceUsageLinkService.detachResource(taskId, link.getId()));
    }

    private void recordLog(Long taskId, String action, String message) {
        logService.recordLog(taskId, action, message, SecurityUtils.getUserId(), SecurityUtils.getUsername());
    }

    private void applyAudit(DispatchTask task) {
        applyAudit(task, false);
    }

    private void applyAudit(DispatchTask task, boolean isNew) {
        String operator = SecurityUtils.getUsername();
        Date now = new Date();
        if (isNew) {
            task.setCreateBy(operator);
            task.setCreateTime(now);
        }
        task.setUpdateBy(operator);
        task.setUpdateTime(now);
    }

    private String generateTaskNo() {
        String seq = Seq.getId();
        return "TASK" + seq.substring(seq.length() - 6);
    }
}