package com.aucma.production.service.impl;

import com.aucma.common.utils.DateUtils;
import com.aucma.production.domain.AndonEvent;
import com.aucma.production.domain.AndonEventAssignment;
import com.aucma.production.domain.AndonEventLog;
import com.aucma.production.domain.AndonRule;
import com.aucma.production.mapper.AndonEventAssignmentMapper;
import com.aucma.production.mapper.AndonEventLogMapper;
import com.aucma.production.mapper.AndonEventMapper;
import com.aucma.production.mapper.AndonRuleMapper;
import com.aucma.production.service.IAndonEventService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.aucma.common.constant.AnDonConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * 安灯事件Service业务层处理
 * 
 * @author Yinq
 * @date 2025-10-28
 */
@Service
public class AndonEventServiceImpl implements IAndonEventService 
{
    @Autowired
    private AndonEventMapper andonEventMapper;

    @Autowired
    private AndonRuleMapper andonRuleMapper;

    @Autowired
    private AndonEventAssignmentMapper andonEventAssignmentMapper;

    @Autowired
    private AndonEventLogMapper andonEventLogMapper;

    /**
     * 查询安灯事件
     * 
     * @param eventId 安灯事件主键
     * @return 安灯事件
     */
    @Override
    public AndonEvent selectAndonEventByEventId(Long eventId)
    {
        return andonEventMapper.selectAndonEventByEventId(eventId);
    }

    /**
     * 查询安灯事件列表
     * 
     * @param andonEvent 安灯事件
     * @return 安灯事件
     */
    @Override
    public List<AndonEvent> selectAndonEventList(AndonEvent andonEvent)
    {
        return andonEventMapper.selectAndonEventList(andonEvent);
    }

    /**
     * 新增安灯事件
     * 
     * @param andonEvent 安灯事件
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertAndonEvent(AndonEvent andonEvent)
    {
        andonEvent.setCreateTime(DateUtils.getNowDate());
        // 在新增时自动匹配规则并计算截止时间与优先级
        applyRuleAndCompute(andonEvent, true);
        int ret = andonEventMapper.insertAndonEvent(andonEvent);
        // 基于规则执行派工（派工即通知）
        autoCreateAssignments(andonEvent);
        // 记录日志：创建事件
        logEvent(andonEvent.getEventId(), AnDonConstants.Operation.CREATE, "事件创建并完成规则计算与派工初始化");
        return ret;
    }

    /**
     * 修改安灯事件
     * 
     * @param andonEvent 安灯事件
     * @return 结果
     */
    @Override
    public int updateAndonEvent(AndonEvent andonEvent)
    {
        andonEvent.setUpdateTime(DateUtils.getNowDate());
        // 在修改时根据最新里程碑时间（如开始处理/确认）补充或重算解决截止时间
        applyRuleAndCompute(andonEvent, false);
        int ret = andonEventMapper.updateAndonEvent(andonEvent);
        // 若事件已取消，级联取消派工
        if (AnDonConstants.EventStatus.CANCELLED.equals(andonEvent.getEventStatus())) {
            cascadeCancelAssignments(andonEvent.getEventId());
            logEvent(andonEvent.getEventId(), AnDonConstants.Operation.CANCEL, "事件取消，已级联取消派工");
        } else {
            logEvent(andonEvent.getEventId(), AnDonConstants.Operation.EDIT, "事件编辑并同步时间计算");
        }
        return ret;
    }

    /**
     * 批量删除安灯事件
     * 
     * @param eventIds 需要删除的安灯事件主键
     * @return 结果
     */
    @Override
    public int deleteAndonEventByEventIds(Long[] eventIds)
    {
        return andonEventMapper.deleteAndonEventByEventIds(eventIds);
    }

    /**
     * 删除安灯事件信息
     * 
     * @param eventId 安灯事件主键
     * @return 结果
     */
    @Override
    public int deleteAndonEventByEventId(Long eventId)
    {
        return andonEventMapper.deleteAndonEventByEventId(eventId);
    }

    /**
     * 基于 ANDON_RULE 规则计算事件的确认/解决截止时间与优先级。
     * - ACK_DEADLINE：创建时间 + ACK_TIMEOUT_MIN（分钟）；仅在新增或缺失时计算。
     * - RESOLVE_DEADLINE：锚点（start→ack→create，可由 remark.resAnchor 指定） + RESOLVE_TIMEOUT_MIN（分钟）。
     * - PRIORITY：若事件未设置，则使用规则的 PRIORITY_DEFAULT。
     */
    private void applyRuleAndCompute(AndonEvent e, boolean isInsert) {
        if (e == null) return;

        // 仅按呼叫类型与有效标记拉取候选规则，然后在内存中按“特征匹配度”挑选最优
        AndonRule query = new AndonRule();
        query.setCallTypeCode(e.getCallTypeCode());
        query.setIsFlag(AnDonConstants.FLAG_VALID);
        List<AndonRule> rules = andonRuleMapper.selectAndonRuleList(query);
        AndonRule bestRule = pickBestRule(rules, e);
        if (bestRule == null) {
            // 无规则命中：保留优先级与截止时间为空或默认
            return;
        }

        Integer ackTimeout = longToInt(bestRule.getAckTimeoutMin());
        Integer resolveTimeout = longToInt(bestRule.getResolveTimeoutMin());
        String resAnchor = parseResAnchor(bestRule.getRemark());

        // 优先级：事件未手动设置时采用默认
        if (e.getPriority() == null && bestRule.getPriorityDefault() != null) {
            e.setPriority(bestRule.getPriorityDefault());
        }

        // 确认截止时间：仅在新增或缺失时计算
        if ((isInsert || e.getAckDeadline() == null) && ackTimeout != null && ackTimeout > 0) {
            Date createTime = e.getCreateTime();
            if (createTime != null) {
                e.setAckDeadline(addMinutes(createTime, ackTimeout));
            }
        }

        // 解决截止时间：依据锚点策略计算；在更新时允许重算以贴合最新里程碑
        if (resolveTimeout != null && resolveTimeout > 0) {
            Date anchorTime = resolveAnchorTime(e, resAnchor);
            if (anchorTime != null) {
                e.setResolveDeadline(addMinutes(anchorTime, resolveTimeout));
            }
        }
    }

    /**
     * 根据规则为事件生成派工记录（优先 NOTIFY_USERS，其次 NOTIFY_ROLES）。
     */
    private void autoCreateAssignments(AndonEvent event) {
        if (event == null || event.getEventId() == null) return;
        AndonRule query = new AndonRule();
        query.setCallTypeCode(event.getCallTypeCode());
        query.setIsFlag(AnDonConstants.FLAG_VALID);
        AndonRule rule = pickBestRule(andonRuleMapper.selectAndonRuleList(query), event);
        if (rule == null) return;

        Date now = DateUtils.getNowDate();
        boolean created = false;
        String mode = parseMode(rule.getRemark()); // single | multi
        String fallback = parseFallback(rule.getRemark()); // role | none

        // 优先使用用户列表
        if (notEmpty(rule.getNotifyUsers())) {
            String[] ids = rule.getNotifyUsers().split(",");
            for (int i = 0; i < ids.length; i++) {
                String userIdStr = ids[i] == null ? null : ids[i].trim();
                if (!notEmpty(userIdStr)) continue;
                try {
                    Long uid = Long.valueOf(userIdStr);
                    AndonEventAssignment assignment = new AndonEventAssignment();
                    assignment.setEventId(event.getEventId());
                    assignment.setAssigneeUserId(uid);
                    assignment.setAssignedTime(now);
                    assignment.setStatus(AnDonConstants.AssignmentStatus.ASSIGNED);
                    // PDA调用时立即设置acceptTime（表示PDA已接单）
                    assignment.setAcceptTime(now);
                    assignment.setIsFlag(AnDonConstants.FLAG_VALID);
                    assignment.setCreateTime(now);
                    andonEventAssignmentMapper.insertAndonEventAssignment(assignment);
                    created = true;
                    if ("single".equals(mode)) break; // 单人模式：仅创建一个派工
                } catch (Exception ignore) { }
            }
        }
        // 若无用户或单人模式未创建且允许角色兜底
        if ((!created) && "role".equals(fallback) && notEmpty(rule.getNotifyRoles())) {
            String[] roles = rule.getNotifyRoles().split(",");
            for (int i = 0; i < roles.length; i++) {
                String rk = roles[i] == null ? null : roles[i].trim();
                if (!notEmpty(rk)) continue;
                AndonEventAssignment assignment = new AndonEventAssignment();
                assignment.setEventId(event.getEventId());
                assignment.setAssigneeRoleKey(rk);
                assignment.setAssignedTime(now);
                assignment.setStatus(AnDonConstants.AssignmentStatus.ASSIGNED);
                // PDA调用时立即设置acceptTime（表示PDA已接单）
                assignment.setAcceptTime(now);
                assignment.setIsFlag(AnDonConstants.FLAG_VALID);
                assignment.setCreateTime(now);
                andonEventAssignmentMapper.insertAndonEventAssignment(assignment);
                created = true;
                if ("single".equals(mode)) break;
            }
        }
        if (created) {
            logEvent(event.getEventId(), AnDonConstants.Operation.ASSIGN, "根据规则创建派工记录");
        }
    }

    /** 解析派工模式：single|multi，默认为 single（兼容当前单人负责人）。 */
    private String parseMode(String remark) {
        String def = "single";
        if (!notEmpty(remark)) return def;
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode node = mapper.readTree(remark);
            JsonNode mode = node.get("mode");
            if (mode != null && notEmpty(mode.asText())) {
                String v = mode.asText();
                if ("single".equalsIgnoreCase(v) || "multi".equalsIgnoreCase(v)) {
                    return v.toLowerCase();
                }
            }
        } catch (Exception ignore) { }
        return def;
    }

    /** 解析兜底策略：role|none，默认 role。 */
    private String parseFallback(String remark) {
        String def = "role";
        if (!notEmpty(remark)) return def;
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode node = mapper.readTree(remark);
            JsonNode fb = node.get("fallback");
            if (fb != null && notEmpty(fb.asText())) {
                String v = fb.asText();
                if ("role".equalsIgnoreCase(v) || "none".equalsIgnoreCase(v)) {
                    return v.toLowerCase();
                }
            }
        } catch (Exception ignore) { }
        return def;
    }

    /** 事件取消时级联取消派工。 */
    private void cascadeCancelAssignments(Long eventId) {
        if (eventId == null) return;
        AndonEventAssignment filter = new AndonEventAssignment();
        filter.setEventId(eventId);
        List<AndonEventAssignment> list = andonEventAssignmentMapper.selectAndonEventAssignmentList(filter);
        if (list == null) return;
        Date now = DateUtils.getNowDate();
        for (AndonEventAssignment a : list) {
            if (a == null) continue;
            a.setStatus(AnDonConstants.AssignmentStatus.CANCELLED);
            a.setUpdateTime(now);
            andonEventAssignmentMapper.updateAndonEventAssignment(a);
        }
    }

    /** 写入事件日志。 */
    private void logEvent(Long eventId, String operation, String content) {
        if (eventId == null || !notEmpty(operation)) return;
        AndonEventLog log = new AndonEventLog();
        log.setEventId(eventId);
        log.setOperation(operation);
        log.setContent(content);
        log.setOperationTime(DateUtils.getNowDate());
        andonEventLogMapper.insertAndonEventLog(log);
    }

    /** 选择匹配度最高的规则：按产线/工位/班组/触发源类型的精确匹配数排序。 */
    private AndonRule pickBestRule(List<AndonRule> rules, AndonEvent e) {
        if (rules == null || rules.isEmpty() || e == null) return null;
        AndonRule best = null;
        int bestScore = -1;
        for (AndonRule r : rules) {
            int score = 0;
            if (notEmpty(r.getProductLineCode()) && r.getProductLineCode().equals(e.getProductLineCode())) score++;
            if (notEmpty(r.getStationCode()) && r.getStationCode().equals(e.getStationCode())) score++;
            if (notEmpty(r.getTeamCode()) && r.getTeamCode().equals(e.getTeamCode())) score++;
            if (notEmpty(r.getSourceType()) && r.getSourceType().equals(e.getSourceType())) score++;
            if (score > bestScore) {
                bestScore = score;
                best = r;
            }
        }
        return best;
    }

    /** 解析规则 remark 中的 resAnchor（start|ack|create），默认 start。 */
    private String parseResAnchor(String remark) {
        String def = "start";
        if (!notEmpty(remark)) return def;
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode node = mapper.readTree(remark);
            JsonNode anchor = node.get("resAnchor");
            if (anchor != null && notEmpty(anchor.asText())) {
                String v = anchor.asText();
                if ("start".equalsIgnoreCase(v) || "ack".equalsIgnoreCase(v) || "create".equalsIgnoreCase(v)) {
                    return v.toLowerCase();
                }
            }
        } catch (Exception ignore) { }
        return def;
    }

    /** 根据策略选择解决时限的起算锚点时间。 */
    private Date resolveAnchorTime(AndonEvent e, String resAnchor) {
        if (e == null) return null;
        String a = (notEmpty(resAnchor) ? resAnchor : "start");
        if ("start".equals(a)) {
            if (e.getResponseStartTime() != null) return e.getResponseStartTime();
            if (e.getAckTime() != null) return e.getAckTime();
            return e.getCreateTime();
        } else if ("ack".equals(a)) {
            if (e.getAckTime() != null) return e.getAckTime();
            return e.getCreateTime();
        } else { // create
            return e.getCreateTime();
        }
    }

    private boolean notEmpty(String s) { return s != null && !s.trim().isEmpty(); }
    private Integer longToInt(Long v) { return v == null ? null : v.intValue(); }
    private Date addMinutes(Date base, int minutes) { return base == null ? null : new Date(base.getTime() + minutes * 60L * 1000L); }

    /**
     * 确认安灯事件：将状态由 PENDING 转为 PROCESSING，记录确认人与确认时间。
     */
    @Override
    public int acknowledgeEvent(Long eventId, String ackBy) {
        if (eventId == null) return 0;
        AndonEvent event = andonEventMapper.selectAndonEventByEventId(eventId);
        if (event == null) return 0;
        
        Date now = DateUtils.getNowDate();
        event.setAckBy(ackBy);
        event.setAckTime(now);
        event.setEventStatus(AnDonConstants.EventStatus.PROCESSING);
        event.setUpdateTime(now);
        
        int ret = andonEventMapper.updateAndonEvent(event);
        logEvent(eventId, AnDonConstants.Operation.ACK, "事件已确认，确认人：" + ackBy);
        return ret;
    }

    /**
     * 开始处理安灯事件：记录开始处理时间，将状态置为 PROCESSING。
     */
    @Override
    public int startProcessEvent(Long eventId, String operator) {
        if (eventId == null) return 0;
        AndonEvent event = andonEventMapper.selectAndonEventByEventId(eventId);
        if (event == null) return 0;
        
        Date now = DateUtils.getNowDate();
        event.setResponseStartTime(now);
        event.setEventStatus(AnDonConstants.EventStatus.PROCESSING);
        event.setUpdateTime(now);
        
        // 重算解决截止时间（以开始处理时间为锤点）
        applyRuleAndCompute(event, false);
        
        int ret = andonEventMapper.updateAndonEvent(event);
        logEvent(eventId, AnDonConstants.Operation.EDIT, "开始处理，操作人：" + operator);
        return ret;
    }

    /**
     * 完成安灯事件：记录处理完成时间与解决措施，将状态置为 RESOLVED。
     */
    @Override
    public int completeEvent(Long eventId, String resolution, String operator) {
        if (eventId == null) return 0;
        AndonEvent event = andonEventMapper.selectAndonEventByEventId(eventId);
        if (event == null) return 0;
        
        Date now = DateUtils.getNowDate();
        event.setResponseEndTime(now);
        event.setResolution(resolution);
        event.setEventStatus(AnDonConstants.EventStatus.RESOLVED);
        event.setUpdateTime(now);
        
        int ret = andonEventMapper.updateAndonEvent(event);
        logEvent(eventId, AnDonConstants.Operation.RESOLVE, "事件已完成，操作人：" + operator);
        return ret;
    }

    /**
     * 取消安灯事件：记录取消原因，将状态置为 CANCELLED，级联取消派工。
     */
    @Override
    public int cancelEvent(Long eventId, String cancelReason, String operator) {
        if (eventId == null) return 0;
        AndonEvent event = andonEventMapper.selectAndonEventByEventId(eventId);
        if (event == null) return 0;
        
        Date now = DateUtils.getNowDate();
        event.setCancelReason(cancelReason);
        event.setEventStatus(AnDonConstants.EventStatus.CANCELLED);
        event.setUpdateTime(now);
        
        int ret = andonEventMapper.updateAndonEvent(event);
        cascadeCancelAssignments(eventId);
        logEvent(eventId, AnDonConstants.Operation.CANCEL, "事件已取消，操作人：" + operator + "，原因：" + (cancelReason != null ? cancelReason : "未填写"));
        return ret;
    }
}
