package com.zksy.base.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.zksy.base.domain.HazardHiddenAccount;
import com.zksy.base.dto.HazardPageInDTO;
import com.zksy.base.mapper.HazardHiddenAccountMapper;
import com.zksy.base.service.BusinessWorkOrderService;
import com.zksy.base.service.HazardHiddenAccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.zksy.common.utils.StringUtils;
import com.zksy.common.exception.ServiceException;
import com.zksy.system.service.ISysDictDataService;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class HazardHiddenAccountServiceImpl extends ServiceImpl<HazardHiddenAccountMapper, HazardHiddenAccount>
        implements HazardHiddenAccountService {

    @Autowired
    private ISysDictDataService dictDataService;

    @Autowired
    private BusinessWorkOrderService businessWorkOrderService;

    @Override
    public Page<HazardHiddenAccount> findByPage(HazardPageInDTO dto) {
        Page<HazardHiddenAccount> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(dto.getHazardType() != null && !dto.getHazardType().isEmpty(), HazardHiddenAccount::getHazardType, dto.getHazardType());
        wrapper.eq(dto.getHazardLevel() != null && !dto.getHazardLevel().isEmpty(), HazardHiddenAccount::getHazardLevel, dto.getHazardLevel());
        wrapper.eq(dto.getHazardStatus() != null && !dto.getHazardStatus().isEmpty(), HazardHiddenAccount::getHazardStatus, dto.getHazardStatus());
        wrapper.eq(dto.getIsSubmitted() != null, HazardHiddenAccount::getIsSubmitted, dto.getIsSubmitted());
        wrapper.eq(dto.getNetworkId() != null && !dto.getNetworkId().isEmpty(), HazardHiddenAccount::getNetworkId, dto.getNetworkId());
        wrapper.eq(dto.getEquipmentId() != null && !dto.getEquipmentId().isEmpty(), HazardHiddenAccount::getEquipmentId, dto.getEquipmentId());
        wrapper.orderByDesc(HazardHiddenAccount::getCreateTime);
        return this.page(page, wrapper);
    }

    @Override
    public boolean saveWithCheck(HazardHiddenAccount entity) {
        // 新增的隐患记录默认未报送
        entity.setIsSubmitted(0);
        entity.setSubmitTime(null);
        // 默认状态为"待核查"
        if (entity.getHazardStatus() == null || entity.getHazardStatus().isEmpty()) {
            entity.setHazardStatus("待核查");
        }
        LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HazardHiddenAccount::getHazardNo, entity.getHazardNo());
        if (this.count(wrapper) > 0) {
            throw new ServiceException("隐患编号已存在：" + entity.getHazardNo());
        }
        try {
            return this.save(entity);
        } catch (Exception e) {
            if (e.getMessage() != null && e.getMessage().contains("uk_hazard_no")) {
                throw new ServiceException("隐患编号已存在（含已删除记录），请更换：" + entity.getHazardNo());
            }
            throw e;
        }
    }

    @Override
    @Transactional
    public boolean updateWithCheck(HazardHiddenAccount entity) {
        if (this.getById(entity.getHazardId()) == null) {
            throw new ServiceException("隐患信息不存在：" + entity.getHazardId());
        }
        String newNo = entity.getHazardNo();
        if (newNo != null) {
            LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(HazardHiddenAccount::getHazardNo, newNo)
                    .ne(HazardHiddenAccount::getHazardId, entity.getHazardId());
            if (this.count(wrapper) > 0) {
                throw new ServiceException("隐患编号已被使用：" + newNo);
            }
        }
        try {
            return this.updateById(entity);
        } catch (Exception e) {
            if (e.getMessage() != null && e.getMessage().contains("uk_hazard_no")) {
                throw new ServiceException("隐患编号冲突（含已删除记录），请更换编码");
            }
            throw e;
        }
    }

    @Override
    @Transactional
    public Long submitHazard(String hazardId) {
        HazardHiddenAccount hazard = this.getById(hazardId);
        if (hazard == null) {
            return null;
        }
        hazard.setIsSubmitted(1);
        hazard.setSubmitTime(LocalDateTime.now());
        this.updateById(hazard);

        // 当隐患关联了设备时，自动创建工单
        if (hazard.getEquipmentId() != null && !hazard.getEquipmentId().isEmpty()) {
            try {
                Date planFinishTime = null;
                if (hazard.getRectifyDeadline() != null) {
                    planFinishTime = java.sql.Date.valueOf(hazard.getRectifyDeadline());
                }
                Long orderId = businessWorkOrderService.createWorkOrderFromHazard(
                        hazard.getHazardId(),
                        hazard.getHazardNo(),
                        hazard.getHazardDesc(),
                        hazard.getEquipmentId(),
                        hazard.getHazardLevel(),
                        hazard.getHazardImages(),
                        planFinishTime
                );
                log.info("隐患报送[{}]自动创建工单成功，工单ID={}", hazardId, orderId);
                return orderId;
            } catch (Exception e) {
                log.error("隐患报送[{}]自动创建工单失败", hazardId, e);
                throw new ServiceException("报送成功但工单创建失败：" + e.getMessage());
            }
        }
        return null;
    }

    @Override
    public List<HazardHiddenAccount> getActiveMapPoints() {
        LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(HazardHiddenAccount::getHazardStatus, "已整改");
        wrapper.isNotNull(HazardHiddenAccount::getLongitude);
        wrapper.isNotNull(HazardHiddenAccount::getLatitude);
        wrapper.select(HazardHiddenAccount::getHazardId, HazardHiddenAccount::getHazardNo,
                HazardHiddenAccount::getHazardType, HazardHiddenAccount::getHazardLevel,
                HazardHiddenAccount::getHazardStatus, HazardHiddenAccount::getHazardDesc,
                HazardHiddenAccount::getLongitude, HazardHiddenAccount::getLatitude,
                HazardHiddenAccount::getRectifyFeedback, HazardHiddenAccount::getReportTime,
                HazardHiddenAccount::getReportUnit, HazardHiddenAccount::getRectifyPlan,
                HazardHiddenAccount::getRemark);
        return this.list(wrapper);
    }

    @Override
    public List<Map<String, Object>> statisticsByType(String startDate, String endDate) {
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        Map<String, Long> grouped = list.stream()
                .collect(Collectors.groupingBy(h -> h.getHazardType() != null ? h.getHazardType() : "未知", Collectors.counting()));
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map.Entry<String, Long> entry : grouped.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", entry.getKey());
            item.put("value", entry.getValue());
            result.add(item);
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> statisticsByStatus(String startDate, String endDate) {
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        Map<String, Long> grouped = list.stream()
                .collect(Collectors.groupingBy(h -> h.getHazardStatus() != null ? h.getHazardStatus() : "未知", Collectors.counting()));
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map.Entry<String, Long> entry : grouped.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", entry.getKey());
            item.put("value", entry.getValue());
            result.add(item);
        }
        return result;
    }

    @Override
    public Map<String, Object> statisticsOverview(String startDate, String endDate) {
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        long total = list.size();
        long rectified = list.stream()
                .filter(h -> "3".equals(h.getHazardStatus()))
                .count();

        // 按隐患等级分组
        Map<String, Long> levelGrouped = list.stream()
                .collect(Collectors.groupingBy(h -> h.getHazardLevel() != null ? h.getHazardLevel() : "未知", Collectors.counting()));
        List<Map<String, Object>> byLevel = new ArrayList<>();
        for (Map.Entry<String, Long> entry : levelGrouped.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("level", entry.getKey());
            item.put("count", entry.getValue());
            byLevel.add(item);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("rectified", rectified);
        result.put("byLevel", byLevel);
        return result;
    }

    @Override
    public List<Map<String, Object>> statisticsByLevel(String startDate, String endDate) {
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        Map<String, Long> grouped = list.stream()
                .collect(Collectors.groupingBy(h -> h.getHazardLevel() != null ? h.getHazardLevel() : "未知", Collectors.counting()));
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map.Entry<String, Long> entry : grouped.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", entry.getKey());
            item.put("value", entry.getValue());
            result.add(item);
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> statisticsByRegion(String startDate, String endDate) {
        // 无日期参数时默认最近7天
        if (!StringUtils.hasText(startDate)) {
            startDate = LocalDate.now().minusDays(6).toString();
        }
        if (!StringUtils.hasText(endDate)) {
            endDate = LocalDate.now().toString();
        }
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        Map<String, Long> grouped = list.stream()
                .collect(Collectors.groupingBy(h -> h.getReportUnit() != null && !h.getReportUnit().isEmpty()
                        ? h.getReportUnit() : "未知区域", Collectors.counting()));
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map.Entry<String, Long> entry : grouped.entrySet()) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", entry.getKey());
            item.put("value", entry.getValue());
            result.add(item);
        }
        return result;
    }

    @Override
    public Map<String, Object> statisticsTypeTimeline(String startDate, String endDate) {
        // 无日期参数时默认最近7天
        LocalDate rangeStart, rangeEnd;
        if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
            rangeStart = LocalDate.parse(startDate);
            rangeEnd = LocalDate.parse(endDate);
        } else {
            rangeEnd = LocalDate.now();
            rangeStart = rangeEnd.minusDays(6);
            startDate = rangeStart.toString();
            endDate = rangeEnd.toString();
        }
        return buildTimeline(startDate, endDate, rangeStart, rangeEnd,
                HazardHiddenAccount::getHazardType, "hazard_type");
    }

    @Override
    public Map<String, Object> statisticsStatusTimeline(String startDate, String endDate) {
        // 无日期参数时默认最近7天
        LocalDate rangeStart, rangeEnd;
        if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
            rangeStart = LocalDate.parse(startDate);
            rangeEnd = LocalDate.parse(endDate);
        } else {
            rangeEnd = LocalDate.now();
            rangeStart = rangeEnd.minusDays(6);
            startDate = rangeStart.toString();
            endDate = rangeEnd.toString();
        }
        return buildTimeline(startDate, endDate, rangeStart, rangeEnd,
                HazardHiddenAccount::getHazardStatus, "hazard_status");
    }

    /**
     * 通用时间线构建：按 reportTime 天 + 指定字段分组
     *
     * @param startDate  查询起始日期字符串
     * @param endDate    查询结束日期字符串
     * @param rangeStart 日期轴起点
     * @param rangeEnd   日期轴终点
     * @param classifier 分组字段 (hazardType / hazardStatus)
     * @param dictType   字典类型，用于将字典值转为标签 (hazard_type / hazard_status)
     * @return { categories: [...], series: [{name: 中文标签, data}, ...] }
     */
    private Map<String, Object> buildTimeline(String startDate, String endDate,
                                              LocalDate rangeStart, LocalDate rangeEnd,
                                              java.util.function.Function<HazardHiddenAccount, String> classifier,
                                              String dictType) {
        List<HazardHiddenAccount> list = this.list(buildDateWrapper(startDate, endDate));
        List<String> categories = generateDateRange(rangeStart, rangeEnd);

        // 二级分组: 字段值 → 日期 → 数量
        Map<String, Map<LocalDate, Long>> fieldDateMap = list.stream()
                .collect(Collectors.groupingBy(
                        h -> {
                            String val = classifier.apply(h);
                            return val != null && !val.isEmpty() ? val : "未知";
                        },
                        Collectors.groupingBy(
                                h -> h.getReportTime() != null ? h.getReportTime().toLocalDate() : LocalDate.now(),
                                Collectors.counting()
                        )
                ));

        List<Map<String, Object>> series = new ArrayList<>();
        for (Map.Entry<String, Map<LocalDate, Long>> entry : fieldDateMap.entrySet()) {
            Map<String, Object> s = new HashMap<>();
            // 通过字典服务将字典值转为中文标签
            String label = dictDataService.selectDictLabel(dictType, entry.getKey());
            s.put("name", StringUtils.hasText(label) ? label : entry.getKey());
            Map<LocalDate, Long> dateCount = entry.getValue();
            List<Long> data = new ArrayList<>();
            for (String dateStr : categories) {
                LocalDate d = LocalDate.parse(dateStr);
                data.add(dateCount.getOrDefault(d, 0L));
            }
            s.put("data", data);
            series.add(s);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("categories", categories);
        result.put("series", series);
        return result;
    }

    /**
     * 生成日期序列（包含首尾）
     */
    private List<String> generateDateRange(LocalDate start, LocalDate end) {
        List<String> dates = new ArrayList<>();
        LocalDate current = start;
        while (!current.isAfter(end)) {
            dates.add(current.toString());
            current = current.plusDays(1);
        }
        return dates;
    }

    /**
     * 根据日期范围构建查询条件，使用 reportTime 字段过滤
     */
    private LambdaQueryWrapper<HazardHiddenAccount> buildDateWrapper(String startDate, String endDate) {
        LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.hasText(startDate)) {
            wrapper.ge(HazardHiddenAccount::getReportTime, LocalDate.parse(startDate).atStartOfDay());
        }
        if (StringUtils.hasText(endDate)) {
            wrapper.le(HazardHiddenAccount::getReportTime, LocalDate.parse(endDate).atTime(LocalTime.MAX));
        }
        return wrapper;
    }
}
