package com.ruoyi.web.modules.industryservice.service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.industryservice.domain.BizYakHealthSourceRecord;
import com.ruoyi.web.modules.industryservice.domain.dto.YakDiseaseWarningDto;
import com.ruoyi.web.modules.industryservice.mapper.BizYakHealthSourceRecordMapper;
import com.ruoyi.web.modules.industryservice.support.YakDiseaseWarningAnalysisRules;

/**
 * 牦牛疾病预警 — 健康源规则分析器。
 * <p>
 * 仅处理 {@code analyzed_flag=0} 的健康源记录；分析后一律标记已分析，避免重复告警。<br>
 * <b>集中预警规则</b>（阈值见 {@link YakDiseaseWarningAnalysisRules#CONCENTRATION_THRESHOLD}）：
 * <ul>
 *   <li>检疫：按「地区 + 自然日」分组，组内检疫结果含异常/阳性等关键字的条数 ≥ 阈值 → 写 type=3；</li>
 *   <li>诊疗：按「地区 + 自然日」分组，组内全部诊疗条数 ≥ 阈值 → 写 type=4；</li>
 *   <li>养殖过程：本期仅标记已分析，不生成预警（体温/运动量等可后续扩展）。</li>
 * </ul>
 * 自然日按 {@code Asia/Shanghai} 的 {@code yyyy-MM-dd} 计算。
 * </p>
 */
@Service
public class YakDiseaseWarningAnalyzer
{
    private static final SimpleDateFormat DAY_KEY = createDayKeyFormat();

    @Autowired
    private BizYakHealthSourceRecordMapper healthSourceRecordMapper;

    @Autowired
    private YakDiseaseWarningSyncTxService warningSyncTxService;

    /**
     * 对未分析健康源执行一轮分析并生成预警。
     *
     * @param syncTime 本轮同步时间，写入预警 {@code last_sync_time}，并作时间缺省回退
     * @return 本轮新插入的预警条数
     */
    @Transactional(rollbackFor = Exception.class)
    public int analyzeAndGenerate(Date syncTime)
    {
        int created = 0;
        created += analyzeQuarantine(syncTime);
        created += analyzeDiagnosis(syncTime);
        markBreedingAnalyzed();
        return created;
    }

    /** 检疫集中预警（仅统计异常/阳性命中）。 */
    private int analyzeQuarantine(Date syncTime)
    {
        List<BizYakHealthSourceRecord> rows = healthSourceRecordMapper
                .selectUnanalyzedByType(YakDiseaseWarningAnalysisRules.RECORD_TYPE_QUARANTINE);
        return analyzeConcentration(rows, syncTime, true);
    }

    /** 诊疗集中预警（组内全部记录计入条数）。 */
    private int analyzeDiagnosis(Date syncTime)
    {
        List<BizYakHealthSourceRecord> rows = healthSourceRecordMapper
                .selectUnanalyzedByType(YakDiseaseWarningAnalysisRules.RECORD_TYPE_DIAGNOSIS);
        return analyzeConcentration(rows, syncTime, false);
    }

    /**
     * 按地区+日分组后判定是否达集中阈值；无论是否写预警，分组内记录均标记已分析。
     *
     * @param quarantineMode true=检疫模式（过滤异常结果）；false=诊疗模式（全量计入）
     */
    private int analyzeConcentration(List<BizYakHealthSourceRecord> rows, Date syncTime, boolean quarantineMode)
    {
        if (rows == null || rows.isEmpty())
        {
            return 0;
        }
        Map<String, List<BizYakHealthSourceRecord>> groups = groupByRegionDay(rows);
        int created = 0;
        for (Map.Entry<String, List<BizYakHealthSourceRecord>> entry : groups.entrySet())
        {
            List<BizYakHealthSourceRecord> group = entry.getValue();
            List<BizYakHealthSourceRecord> hits = new ArrayList<>();
            if (quarantineMode)
            {
                for (BizYakHealthSourceRecord row : group)
                {
                    if (YakDiseaseWarningAnalysisRules.isAbnormalQuarantineResult(row.getQuarantineResult()))
                    {
                        hits.add(row);
                    }
                }
            }
            else
            {
                hits.addAll(group);
            }
            if (hits.size() >= YakDiseaseWarningAnalysisRules.CONCENTRATION_THRESHOLD)
            {
                YakDiseaseWarningDto dto = buildWarningDto(group, hits, syncTime, quarantineMode);
                if (warningSyncTxService.persistAlert(dto, syncTime))
                {
                    created++;
                }
            }
            // 已参与本轮分组的记录均标记，避免下次重复扫描
            markAnalyzed(group);
        }
        return created;
    }

    /** 养殖过程本期不生成预警，仅消化未分析标记。 */
    private void markBreedingAnalyzed()
    {
        List<BizYakHealthSourceRecord> rows = healthSourceRecordMapper
                .selectUnanalyzedByType(YakDiseaseWarningAnalysisRules.RECORD_TYPE_BREEDING);
        markAnalyzed(rows);
    }

    private void markAnalyzed(List<BizYakHealthSourceRecord> rows)
    {
        if (rows == null || rows.isEmpty())
        {
            return;
        }
        List<Long> ids = new ArrayList<>(rows.size());
        for (BizYakHealthSourceRecord row : rows)
        {
            ids.add(row.getId());
        }
        healthSourceRecordMapper.markAnalyzedByIds(ids);
    }

    /**
     * 组装预警 DTO：牧场取命中首条农场名，预警时间取命中记录中最晚的 {@code recordTime}。
     */
    private YakDiseaseWarningDto buildWarningDto(List<BizYakHealthSourceRecord> group,
            List<BizYakHealthSourceRecord> hits, Date syncTime, boolean quarantineMode)
    {
        BizYakHealthSourceRecord anchor = hits.get(0);
        String region = StringUtils.isNotEmpty(anchor.getRegionKey()) ? anchor.getRegionKey() : "未知地区";
        String day = dayKey(anchor.getRecordTime());
        YakDiseaseWarningDto dto = new YakDiseaseWarningDto();
        dto.setPastureName(anchor.getFarmName());
        dto.setAlertTime(latestTime(hits, syncTime));
        dto.setWarningType(quarantineMode ? YakDiseaseWarningAnalysisRules.WARNING_TYPE_QUARANTINE
                : YakDiseaseWarningAnalysisRules.WARNING_TYPE_DIAGNOSIS);
        dto.setAlertMessage(buildConcentrationMessage(quarantineMode, region, day, hits.size()));
        return dto;
    }

    /** 例：{@code 2026-07-20 XX地区检疫结果异常或阳性记录集中（3条），触发健康预警。} */
    private static String buildConcentrationMessage(boolean quarantineMode, String region, String day, int count)
    {
        String typeLabel = quarantineMode ? "检疫结果异常或阳性" : "发病";
        return day + " " + region + " 地区" + typeLabel + "记录集中（" + count + "条），触发健康预警。";
    }

    /** 分组键：{@code regionKey|yyyy-MM-dd}。 */
    private static Map<String, List<BizYakHealthSourceRecord>> groupByRegionDay(List<BizYakHealthSourceRecord> rows)
    {
        Map<String, List<BizYakHealthSourceRecord>> groups = new LinkedHashMap<>();
        for (BizYakHealthSourceRecord row : rows)
        {
            String key = StringUtils.nvl(row.getRegionKey(), "") + "|" + dayKey(row.getRecordTime());
            groups.computeIfAbsent(key, k -> new ArrayList<>()).add(row);
        }
        return groups;
    }

    private static Date latestTime(List<BizYakHealthSourceRecord> hits, Date fallback)
    {
        Date latest = fallback;
        for (BizYakHealthSourceRecord row : hits)
        {
            if (row.getRecordTime() != null && (latest == null || row.getRecordTime().after(latest)))
            {
                latest = row.getRecordTime();
            }
        }
        return latest != null ? latest : fallback;
    }

    private static String dayKey(Date time)
    {
        if (time == null)
        {
            return "";
        }
        synchronized (DAY_KEY)
        {
            return DAY_KEY.format(time);
        }
    }

    private static SimpleDateFormat createDayKeyFormat()
    {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        df.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return df;
    }
}
