package com.zksy.dispatch.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zksy.common.exception.ServiceException;
import com.zksy.common.utils.SecurityUtils;
import com.zksy.dispatch.domain.DisEvent;
import com.zksy.dispatch.domain.DisEventReport;
import com.zksy.dispatch.domain.DisEventTrack;
import com.zksy.dispatch.domain.DisEventTrackLog;
import com.zksy.dispatch.domain.DisEventTransfer;
import com.zksy.dispatch.domain.DisEventUrge;
import com.zksy.dispatch.domain.DisEventVerify;
import com.zksy.dispatch.dto.DisEventPageDTO;
import com.zksy.dispatch.dto.DisEventVO;
import com.zksy.dispatch.dto.EventStatsVO;
import com.zksy.dispatch.mapper.DisEventMapper;
import com.zksy.dispatch.mapper.DisEventReportMapper;
import com.zksy.dispatch.mapper.DisEventTrackLogMapper;
import com.zksy.dispatch.mapper.DisEventTrackMapper;
import com.zksy.dispatch.mapper.DisEventTransferMapper;
import com.zksy.dispatch.mapper.DisEventUrgeMapper;
import com.zksy.dispatch.mapper.DisEventVerifyMapper;
import com.zksy.dispatch.service.DisEventService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 应急事件统一档案 Service 实现（全生命周期闭环）
 * 状态机：待受理 -> 待核实 -> 已核实 -> 处置中 -> 已解除 -> 已关闭；不实事件为终止态
 */
@Slf4j
@Service
public class DisEventServiceImpl extends ServiceImpl<DisEventMapper, DisEvent>
        implements DisEventService {

    /** 事件状态常量 */
    private static final String STATUS_WAIT_ACCEPT = "待受理";
    private static final String STATUS_WAIT_VERIFY = "待核实";
    private static final String STATUS_VERIFIED = "已核实";
    private static final String STATUS_PROCESSING = "处置中";
    private static final String STATUS_RELEASED = "已解除";
    private static final String STATUS_CLOSED = "已关闭";
    private static final String STATUS_FALSE = "不实事件";

    /** 事件编号格式：SJ+yyyyMMddHHmmss */
    private static final DateTimeFormatter NO_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    @Autowired
    private DisEventReportMapper reportMapper;

    @Autowired
    private DisEventVerifyMapper verifyMapper;

    @Autowired
    private DisEventTransferMapper transferMapper;

    @Autowired
    private DisEventTrackMapper trackMapper;

    @Autowired
    private DisEventTrackLogMapper trackLogMapper;

    @Autowired
    private DisEventUrgeMapper urgeMapper;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DisEvent saveReport(DisEvent event) {
        LocalDateTime now = LocalDateTime.now();
        // 自动生成全局唯一事件编号
        event.setEventNo("SJ" + now.format(NO_FORMAT));
        event.setStatus(STATUS_WAIT_ACCEPT);
        event.setReportTime(now);
        event.setCreateTime(now);
        event.setCreateBy(currentUser());
        save(event);
        // 同步写接报明细（挂靠主表）
        DisEventReport report = new DisEventReport();
        report.setRelEventId(event.getId());
        report.setEventNo(event.getEventNo());
        report.setSource(event.getSource());
        report.setLocation(event.getLocation());
        report.setHappenTime(event.getHappenTime());
        report.setDescription(event.getDescription());
        report.setReporter(event.getReporter());
        report.setReportTime(event.getReportTime());
        report.setChannel(event.getChannel());
        report.setStatus(STATUS_WAIT_ACCEPT);
        report.setCreateTime(now);
        reportMapper.insert(report);
        return event;
    }

    @Override
    public Page<DisEventVO> findByPage(DisEventPageDTO dto) {
        Page<DisEvent> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<DisEvent> wrapper = buildWrapper(dto);
        wrapper.orderByDesc(DisEvent::getHappenTime).orderByDesc(DisEvent::getCreateTime);
        page(page, wrapper);

        Page<DisEventVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        List<DisEvent> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            voPage.setRecords(Collections.emptyList());
            return voPage;
        }
        List<Long> ids = records.stream().map(DisEvent::getId).collect(Collectors.toList());
        Map<Long, DisEventTransfer> transferMap = latestTransferMap(ids);
        Map<Long, DisEventTrack> trackMap = latestTrackMap(ids);
        List<DisEventVO> vos = records.stream().map(e -> {
            DisEventVO vo = new DisEventVO();
            BeanUtils.copyProperties(e, vo);
            DisEventTransfer t = transferMap.get(e.getId());
            if (t != null) {
                vo.setTransferDept(t.getTransferDept());
                vo.setReportTargets(t.getReportTargets());
                vo.setTransferTime(t.getTransferTime());
                vo.setTransferId(t.getId());
            }
            DisEventTrack tk = trackMap.get(e.getId());
            if (tk != null) {
                vo.setLastVisitTime(tk.getLastVisitTime());
                vo.setNextVisitTime(tk.getNextVisitTime());
                vo.setTrackId(tk.getId());
            }
            return vo;
        }).collect(Collectors.toList());
        voPage.setRecords(vos);
        return voPage;
    }

    @Override
    public DisEventVO getDetail(Long id) {
        DisEvent e = getById(id);
        if (e == null) {
            throw new ServiceException("事件不存在：" + id);
        }
        DisEventVO vo = new DisEventVO();
        BeanUtils.copyProperties(e, vo);
        Map<String, List<Map<String, Object>>> process = new HashMap<>();
        process.put("transferList", listMap(transferMapper.selectList(
                new LambdaQueryWrapper<DisEventTransfer>()
                        .eq(DisEventTransfer::getRelEventId, id)
                        .orderByDesc(DisEventTransfer::getId))));
        process.put("verifyList", listMap(verifyMapper.selectList(
                new LambdaQueryWrapper<DisEventVerify>()
                        .eq(DisEventVerify::getRelEventId, id)
                        .orderByDesc(DisEventVerify::getId))));
        process.put("trackList", listMap(trackMapper.selectList(
                new LambdaQueryWrapper<DisEventTrack>()
                        .eq(DisEventTrack::getRelEventId, id)
                        .orderByDesc(DisEventTrack::getId))));
        process.put("urgeList", listMap(urgeMapper.selectList(
                new LambdaQueryWrapper<DisEventUrge>()
                        .eq(DisEventUrge::getRelEventId, id)
                        .orderByDesc(DisEventUrge::getId))));
        vo.setProcess(process);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void accept(Long id) {
        DisEvent db = requireEvent(id);
        if (!STATUS_WAIT_ACCEPT.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可受理");
        }
        LocalDateTime now = LocalDateTime.now();
        db.setStatus(STATUS_WAIT_VERIFY);
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
        // 同步接报明细状态
        DisEventReport report = reportMapper.selectOne(
                new LambdaQueryWrapper<DisEventReport>()
                        .eq(DisEventReport::getRelEventId, id)
                        .orderByDesc(DisEventReport::getId)
                        .last("LIMIT 1"));
        if (report != null) {
            report.setStatus(STATUS_WAIT_VERIFY);
            report.setUpdateTime(now);
            reportMapper.updateById(report);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void verify(DisEvent event) {
        DisEvent db = requireEvent(event.getId());
        if (!STATUS_WAIT_VERIFY.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可核实");
        }
        LocalDateTime now = LocalDateTime.now();
        db.setCategory(event.getCategory());
        db.setLevel(event.getLevel());
        db.setJudgeTime(now);
        db.setVerifier(currentUser());
        db.setVerifyTime(now);
        db.setVerifyOpinion(event.getVerifyOpinion());
        // 不实事件为终止态
        db.setStatus("不实".equals(event.getIsReal()) ? STATUS_FALSE : STATUS_VERIFIED);
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
        // 写核实明细（挂靠主表）
        DisEventVerify v = new DisEventVerify();
        v.setRelEventId(db.getId());
        v.setReportId(null);
        v.setEventNo(db.getEventNo());
        v.setCategory(event.getCategory());
        v.setLevel(event.getLevel());
        v.setLocation(db.getLocation());
        v.setDescription(db.getDescription());
        v.setJudgeTime(now);
        v.setVerifier(currentUser());
        v.setVerifyTime(now);
        v.setOpinion(event.getVerifyOpinion());
        v.setIsReal(event.getIsReal());
        v.setStatus(db.getStatus());
        v.setCreateTime(now);
        verifyMapper.insert(v);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transfer(DisEventTransfer transfer) {
        DisEvent db = requireEvent(transfer.getRelEventId());
        if (!STATUS_VERIFIED.equals(db.getStatus()) && !STATUS_PROCESSING.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可转接");
        }
        LocalDateTime now = LocalDateTime.now();
        // 更新主表：责任单位 + 进入处置中
        db.setDepartment(transfer.getTransferDept());
        db.setStatus(STATUS_PROCESSING);
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
        // 写转接明细（挂靠主表）
        DisEventTransfer t = new DisEventTransfer();
        t.setRelEventId(db.getId());
        t.setEventNo(db.getEventNo());
        t.setCategory(db.getCategory());
        t.setLevel(db.getLevel());
        t.setSummary(db.getDescription());
        t.setTransferDept(transfer.getTransferDept());
        t.setTransferRemark(transfer.getTransferRemark());
        t.setTransferTime(now);
        t.setStatus("待报送");
        t.setOperator(currentUser());
        t.setCreateTime(now);
        transferMapper.insert(t);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void report(DisEventTransfer transfer) {
        DisEventTransfer t = transferMapper.selectById(transfer.getId());
        if (t == null) {
            throw new ServiceException("转接记录不存在：" + transfer.getId());
        }
        LocalDateTime now = LocalDateTime.now();
        t.setReportTargets(transfer.getReportTargets());
        t.setAttachPaths(transfer.getAttachPaths());
        if (StringUtils.isBlank(t.getReportEventNo())) {
            t.setReportEventNo("BS" + now.format(NO_FORMAT));
        }
        t.setStatus("已报送");
        t.setUpdateTime(now);
        transferMapper.updateById(t);
        // 事件保持处置中
        if (t.getRelEventId() != null) {
            DisEvent db = getById(t.getRelEventId());
            if (db != null && !STATUS_PROCESSING.equals(db.getStatus())) {
                db.setStatus(STATUS_PROCESSING);
                db.setUpdateTime(now);
                db.setUpdateBy(currentUser());
                updateById(db);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void track(DisEventTrack track) {
        DisEvent db = requireEvent(track.getRelEventId());
        if (!STATUS_PROCESSING.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可跟踪");
        }
        LocalDateTime now = LocalDateTime.now();
        // 写跟踪单（挂靠主表）
        DisEventTrack tk = new DisEventTrack();
        tk.setRelEventId(db.getId());
        tk.setEventNo(db.getEventNo());
        tk.setCategory(db.getCategory());
        tk.setLevel(db.getLevel());
        tk.setLocation(db.getLocation());
        tk.setDescription(db.getDescription());
        tk.setHandler(StringUtils.isNotBlank(track.getHandler()) ? track.getHandler() : currentUser());
        tk.setStatus(STATUS_PROCESSING);
        tk.setLastVisitTime(now);
        tk.setNextVisitTime(track.getNextVisitTime());
        tk.setCreateTime(now);
        trackMapper.insert(tk);
        // 写跟踪日志（时间线展示）
        DisEventTrackLog logRec = new DisEventTrackLog();
        logRec.setTrackId(tk.getId());
        logRec.setEventNo(db.getEventNo());
        logRec.setTitle("跟踪记录");
        logRec.setContent(track.getDescription());
        logRec.setType("primary");
        logRec.setAssessment(track.getAssessment());
        logRec.setNeedUpgrade(track.getNeedUpgrade());
        logRec.setActionTime(now);
        logRec.setCreateTime(now);
        trackLogMapper.insert(logRec);
        // 更新主表处置负责人与回访时间
        db.setHandler(tk.getHandler());
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void release(Long id) {
        DisEvent db = requireEvent(id);
        if (!STATUS_PROCESSING.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可解除");
        }
        LocalDateTime now = LocalDateTime.now();
        db.setStatus(STATUS_RELEASED);
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void close(DisEvent event) {
        DisEvent db = requireEvent(event.getId());
        if (!STATUS_RELEASED.equals(db.getStatus())) {
            throw new ServiceException("当前状态【" + db.getStatus() + "】不可关闭");
        }
        LocalDateTime now = LocalDateTime.now();
        db.setStatus(STATUS_CLOSED);
        db.setCloseReason(event.getCloseReason());
        db.setCloseRemark(event.getCloseRemark());
        db.setCloseTime(now);
        if (event.getLossAmount() != null) {
            db.setLossAmount(event.getLossAmount());
        }
        db.setUpdateTime(now);
        db.setUpdateBy(currentUser());
        updateById(db);
    }

    @Override
    public EventStatsVO stats(DisEventPageDTO dto) {
        List<DisEvent> events = list(buildWrapper(dto));
        EventStatsVO vo = new EventStatsVO();
        vo.setTotalEvents((long) events.size());
        BigDecimal totalLoss = events.stream()
                .map(DisEvent::getLossAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        vo.setTotalLoss(totalLoss);
        long lossCount = events.stream().filter(e -> e.getLossAmount() != null).count();
        vo.setAvgLoss(lossCount == 0 ? BigDecimal.ZERO
                : totalLoss.divide(BigDecimal.valueOf(lossCount), 2, RoundingMode.HALF_UP));
        long majorEvents = events.stream()
                .filter(e -> "重大".equals(e.getLevel()) || "特别重大".equals(e.getLevel()))
                .count();
        vo.setMajorEvents(majorEvents);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateEvent(DisEvent event) {
        DisEvent db = requireEvent(event.getId());
        BeanUtils.copyProperties(event, db,
                "id", "eventNo", "status", "reportTime", "createBy", "createTime", "updateTime", "delFlag");
        db.setUpdateTime(LocalDateTime.now());
        db.setUpdateBy(currentUser());
        updateById(db);
    }

    /**
     * 构建多维条件查询条件（列表与统计共用）
     */
    private LambdaQueryWrapper<DisEvent> buildWrapper(DisEventPageDTO dto) {
        LambdaQueryWrapper<DisEvent> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(dto.getEventNo()), DisEvent::getEventNo, dto.getEventNo());
        wrapper.like(StringUtils.isNotBlank(dto.getEventName()), DisEvent::getEventName, dto.getEventName());
        wrapper.eq(StringUtils.isNotBlank(dto.getEventType()), DisEvent::getEventType, dto.getEventType());
        wrapper.eq(StringUtils.isNotBlank(dto.getCategory()), DisEvent::getCategory, dto.getCategory());
        wrapper.eq(StringUtils.isNotBlank(dto.getLevel()), DisEvent::getLevel, dto.getLevel());
        wrapper.eq(StringUtils.isNotBlank(dto.getSource()), DisEvent::getSource, dto.getSource());
        wrapper.eq(StringUtils.isNotBlank(dto.getRegion()), DisEvent::getRegion, dto.getRegion());
        wrapper.eq(StringUtils.isNotBlank(dto.getDepartment()), DisEvent::getDepartment, dto.getDepartment());
        if (!CollectionUtils.isEmpty(dto.getStatusList())) {
            wrapper.in(DisEvent::getStatus, dto.getStatusList());
        } else {
            wrapper.eq(StringUtils.isNotBlank(dto.getStatus()), DisEvent::getStatus, dto.getStatus());
        }
        if (StringUtils.isNotBlank(dto.getStartDate())) {
            try {
                wrapper.ge(DisEvent::getHappenTime, LocalDate.parse(dto.getStartDate()).atStartOfDay());
            } catch (Exception e) {
                log.warn("开始日期格式错误: {}", dto.getStartDate());
            }
        }
        if (StringUtils.isNotBlank(dto.getEndDate())) {
            try {
                wrapper.lt(DisEvent::getHappenTime, LocalDate.parse(dto.getEndDate()).plusDays(1).atStartOfDay());
            } catch (Exception e) {
                log.warn("结束日期格式错误: {}", dto.getEndDate());
            }
        }
        applyLossRange(wrapper, dto.getLossRange());
        return wrapper;
    }

    /**
     * 损失情况区间条件
     */
    private void applyLossRange(LambdaQueryWrapper<DisEvent> wrapper, String lossRange) {
        if (StringUtils.isBlank(lossRange)) {
            return;
        }
        switch (lossRange) {
            case "100万以下":
                wrapper.lt(DisEvent::getLossAmount, new BigDecimal(100));
                break;
            case "100-500万":
                wrapper.ge(DisEvent::getLossAmount, new BigDecimal(100))
                        .lt(DisEvent::getLossAmount, new BigDecimal(500));
                break;
            case "500-1000万":
                wrapper.ge(DisEvent::getLossAmount, new BigDecimal(500))
                        .lt(DisEvent::getLossAmount, new BigDecimal(1000));
                break;
            case "1000万以上":
                wrapper.ge(DisEvent::getLossAmount, new BigDecimal(1000));
                break;
            default:
                break;
        }
    }

    /**
     * 查询每个事件最近一条转接记录
     */
    private Map<Long, DisEventTransfer> latestTransferMap(List<Long> ids) {
        Map<Long, DisEventTransfer> map = new HashMap<>();
        if (CollectionUtils.isEmpty(ids)) {
            return map;
        }
        List<DisEventTransfer> list = transferMapper.selectList(
                new LambdaQueryWrapper<DisEventTransfer>()
                        .in(DisEventTransfer::getRelEventId, ids)
                        .orderByDesc(DisEventTransfer::getId));
        for (DisEventTransfer t : list) {
            map.putIfAbsent(t.getRelEventId(), t);
        }
        return map;
    }

    /**
     * 查询每个事件最近一条跟踪记录
     */
    private Map<Long, DisEventTrack> latestTrackMap(List<Long> ids) {
        Map<Long, DisEventTrack> map = new HashMap<>();
        if (CollectionUtils.isEmpty(ids)) {
            return map;
        }
        List<DisEventTrack> list = trackMapper.selectList(
                new LambdaQueryWrapper<DisEventTrack>()
                        .in(DisEventTrack::getRelEventId, ids)
                        .orderByDesc(DisEventTrack::getId));
        for (DisEventTrack tk : list) {
            map.putIfAbsent(tk.getRelEventId(), tk);
        }
        return map;
    }

    /**
     * 实体列表转 Map 列表（环节明细序列化）
     */
    private List<Map<String, Object>> listMap(List<?> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list.stream().map(item -> objectMapper.convertValue(item, new TypeReference<Map<String, Object>>() {}))
                .collect(Collectors.toList());
    }

    /**
     * 获取事件并校验存在
     */
    private DisEvent requireEvent(Long id) {
        DisEvent db = getById(id);
        if (db == null) {
            throw new ServiceException("事件不存在：" + id);
        }
        return db;
    }

    /**
     * 当前登录用户（未登录时返回系统）
     */
    private String currentUser() {
        try {
            return SecurityUtils.getUsername();
        } catch (Exception e) {
            return "system";
        }
    }
}
