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

import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.industryservice.domain.BizYakHealthSourceRecord;
import com.ruoyi.web.modules.industryservice.domain.dto.OpenBreedingProcessRecordDto;
import com.ruoyi.web.modules.industryservice.domain.dto.OpenDiagnosisRecordDto;
import com.ruoyi.web.modules.industryservice.domain.dto.OpenQuarantineRecordDto;
import com.ruoyi.web.modules.industryservice.domain.vo.YakAssetSyncResultVo;
import com.ruoyi.web.modules.industryservice.support.HealthRecordOpenApiMapper;
import com.ruoyi.web.modules.industryservice.support.ThirdPartyHealthRecordClient;
import com.ruoyi.web.modules.industryservice.support.YakDiseaseWarningRules;

/**
 * 牦牛疾病预警 — 第三方健康源同步编排。
 * <p>
 * 完整链路：
 * <ol>
 *   <li>调用 OpenAPI 拉取检疫 / 诊疗 / 养殖过程三类记录；</li>
 *   <li>经 {@link HealthRecordOpenApiMapper} 转为本地健康源实体；</li>
 *   <li>按 {@code external_id}（如 {@code Q:123}/{@code D:456}/{@code B:789}）upsert 至
 *       {@code biz_yak_health_source_record}（独立事务，单条失败不影响其他）；</li>
 *   <li>调用 {@link YakDiseaseWarningAnalyzer} 对未分析记录做集中规则判定并写入预警表。</li>
 * </ol>
 * 进程内 {@link AtomicBoolean} 互斥，避免重复点击导致并发同步。
 * </p>
 */
@Service
public class YakDiseaseWarningSyncService
{
    private final AtomicBoolean syncing = new AtomicBoolean(false);

    @Autowired
    private ThirdPartyHealthRecordClient thirdPartyHealthRecordClient;

    @Autowired
    private YakHealthSourceRecordSyncTxService healthSourceRecordSyncTxService;

    @Autowired
    private YakDiseaseWarningAnalyzer warningAnalyzer;

    /**
     * 执行一次完整同步 + 分析。
     *
     * @return 汇总：健康源 insert/update、分析新增预警计入 insert、失败条数与原因
     * @throws ServiceException 已有同步在进行中，或第三方调用整体失败
     */
    public YakAssetSyncResultVo syncFromThirdParty()
    {
        if (!syncing.compareAndSet(false, true))
        {
            throw new ServiceException(YakDiseaseWarningRules.MSG_SYNC_IN_PROGRESS);
        }
        try
        {
            YakAssetSyncResultVo result = new YakAssetSyncResultVo();
            Date syncTime = new Date();
            syncQuarantineRecords(result, syncTime);
            syncDiagnosisRecords(result, syncTime);
            syncBreedingRecords(result, syncTime);
            try
            {
                // 分析产生的预警条数并入 insertCount，便于前端一次展示同步结果
                int warningCreated = warningAnalyzer.analyzeAndGenerate(syncTime);
                result.setInsertCount(result.getInsertCount() + warningCreated);
            }
            catch (Exception e)
            {
                result.setFailCount(result.getFailCount() + 1);
                result.getFailMessages().add("分析生成预警: " + e.getMessage());
            }
            result.setSyncTime(syncTime);
            return result;
        }
        finally
        {
            syncing.set(false);
        }
    }

    /** 拉取并落库检疫记录（异常/阳性后续参与集中预警）。 */
    private void syncQuarantineRecords(YakAssetSyncResultVo result, Date syncTime)
    {
        List<OpenQuarantineRecordDto> records = fetchSafely(() -> thirdPartyHealthRecordClient.fetchQuarantineRecords());
        for (OpenQuarantineRecordDto dto : records)
        {
            persistSource(result, syncTime, () -> HealthRecordOpenApiMapper.fromQuarantine(dto),
                    dto != null ? String.valueOf(dto.getId()) : "?");
        }
    }

    /** 拉取并落库诊疗记录（同区同日条数达阈值则集中预警）。 */
    private void syncDiagnosisRecords(YakAssetSyncResultVo result, Date syncTime)
    {
        List<OpenDiagnosisRecordDto> records = fetchSafely(() -> thirdPartyHealthRecordClient.fetchDiagnosisRecords());
        for (OpenDiagnosisRecordDto dto : records)
        {
            persistSource(result, syncTime, () -> HealthRecordOpenApiMapper.fromDiagnosis(dto),
                    dto != null ? String.valueOf(dto.getId()) : "?");
        }
    }

    /**
     * 拉取并落库养殖过程记录。
     * <p>当前分析阶段仅标记已分析，不单独生成疾病预警（预留体温/运动量等扩展）。</p>
     */
    private void syncBreedingRecords(YakAssetSyncResultVo result, Date syncTime)
    {
        List<OpenBreedingProcessRecordDto> records = fetchSafely(
                () -> thirdPartyHealthRecordClient.fetchBreedingProcessRecords());
        for (OpenBreedingProcessRecordDto dto : records)
        {
            persistSource(result, syncTime, () -> HealthRecordOpenApiMapper.fromBreedingProcess(dto),
                    dto != null ? String.valueOf(dto.getId()) : "?");
        }
    }

    private interface SourceMapper
    {
        BizYakHealthSourceRecord map();
    }

    private interface RecordFetcher<T>
    {
        List<T> fetch();
    }

    /**
     * 拉取第三方列表；{@link ServiceException} 原样抛出，其他异常包装为「第三方服务不可用」。
     */
    private <T> List<T> fetchSafely(RecordFetcher<T> fetcher)
    {
        try
        {
            return fetcher.fetch();
        }
        catch (ServiceException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ServiceException("第三方服务不可用：" + e.getMessage());
        }
    }

    /**
     * 单条健康源落库；失败只记入结果，不中断整批。
     *
     * @param failKey 失败日志标识（通常为第三方记录 ID）
     */
    private void persistSource(YakAssetSyncResultVo result, Date syncTime, SourceMapper mapper, String failKey)
    {
        try
        {
            BizYakHealthSourceRecord row = mapper.map();
            boolean inserted = healthSourceRecordSyncTxService.persistSourceRecord(row, syncTime);
            if (inserted)
            {
                result.setInsertCount(result.getInsertCount() + 1);
            }
            else
            {
                result.setUpdateCount(result.getUpdateCount() + 1);
            }
        }
        catch (Exception e)
        {
            result.setFailCount(result.getFailCount() + 1);
            result.getFailMessages().add(failKey + ": " + e.getMessage());
        }
    }
}
