package com.ycwl.basic.service.pc.orchestrator;
import cn.hutool.core.date.DateUtil;
import com.ycwl.basic.biz.FaceStatusManager;
import com.ycwl.basic.enums.FaceCutStatus;
import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
import com.ycwl.basic.puzzle.dto.PuzzleGenerateRequest;
import com.ycwl.basic.puzzle.dto.PuzzleGenerateResponse;
import com.ycwl.basic.puzzle.dto.PuzzleTemplateDTO;
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
import com.ycwl.basic.puzzle.service.IPuzzleGenerateService;
import com.ycwl.basic.puzzle.service.IPuzzleTemplateService;

import java.util.HashMap;
import java.util.Map;

import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.mapper.FaceSampleAssociationMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.MemberRelationRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.service.pc.helper.FaceMetricsRecorder;
import com.ycwl.basic.service.pc.helper.ScenicConfigFacade;
import com.ycwl.basic.service.pc.processor.BuyStatusProcessor;
import com.ycwl.basic.service.pc.processor.FaceRecoveryStrategy;
import com.ycwl.basic.service.pc.processor.SourceRelationProcessor;
import com.ycwl.basic.service.pc.processor.VideoRecreationHandler;
import com.ycwl.basic.service.task.TaskFaceService;
import com.ycwl.basic.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 人脸匹配流程编排器
 * 将 matchFaceId 的复杂流程拆分为清晰的步骤，每个步骤负责单一职责
 *
 * 主要步骤：
 * 1. 数据准备 - 获取人脸信息、配置、适配器
 * 2. 人脸识别 - 执行人脸搜索和补救逻辑
 * 3. 结果更新 - 更新人脸实体信息
 * 4. 源文件关联 - 处理匹配到的源文件
 * 5. 业务处理 - 免费逻辑、购买状态、任务创建
 * 6. 数据持久化 - 保存关联关系
 *
 * @author Claude
 * @since 2025-10-31
 */
@Slf4j
@Component
public class FaceMatchingOrchestrator {

    @Autowired
    private FaceRepository faceRepository;
    @Autowired
    private FaceMapper faceMapper;
    @Autowired
    private ScenicRepository scenicRepository;
    @Autowired
    private ScenicService scenicService;
    @Autowired
    private TaskFaceService taskFaceService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private SourceMapper sourceMapper;
    @Autowired
    private MemberRelationRepository memberRelationRepository;

    // 辅助类和处理器
    @Autowired
    private FaceMetricsRecorder metricsRecorder;
    @Autowired
    private ScenicConfigFacade scenicConfigFacade;
    @Autowired
    private FaceRecoveryStrategy faceRecoveryStrategy;
    @Autowired
    private SourceRelationProcessor sourceRelationProcessor;
    @Autowired
    private BuyStatusProcessor buyStatusProcessor;
    @Autowired
    private VideoRecreationHandler videoRecreationHandler;
    @Autowired
    private IPuzzleTemplateService puzzleTemplateService;
    @Autowired
    private IPuzzleGenerateService puzzleGenerateService;
    @Autowired
    private FaceStatusManager faceStatusManager;
    @Autowired
    private PuzzleRepository puzzleRepository;
    @Autowired
    private FaceSampleAssociationMapper faceSampleAssociationMapper;

    /**
     * 编排人脸匹配的完整流程
     *
     * @param faceId 人脸ID
     * @param isNew  是否新用户
     * @return 人脸搜索结果
     */
    public SearchFaceRespVo orchestrateMatching(Long faceId, boolean isNew, String scene) {
        if (faceId == null) {
            throw new IllegalArgumentException("faceId 不能为空");
        }

        if (isNew) {
            // 新用户，设置任务状态为待处理
            faceStatusManager.setFaceCutStatus(faceId, FaceCutStatus.CUTTING);
        }

        // 步骤1: 数据准备
        MatchingContext context = prepareMatchingContext(faceId, isNew);
        if (context == null) {
            return null;
        }

        // 记录识别次数
        metricsRecorder.recordRecognitionCount(faceId);

        try {
            // 步骤2: 人脸识别
            SearchFaceRespVo searchResult = executeFaceRecognition(context);
            if (searchResult == null) {
                log.warn("人脸识别返回结果为空，faceId={}", faceId);
                throw new BaseException("人脸识别失败,请换一张试试把~");
            }

            // 执行补救逻辑
            searchResult = faceRecoveryStrategy.executeFaceRecoveryLogic(
                    searchResult, context.scenicConfig, context.faceBodyAdapter, context.face.getScenicId());

            // 关联样本扩展
            expandSampleAssociation(searchResult, context.face.getScenicId(), faceId);

            // 步骤3: 更新人脸结果
            updateFaceResult(context.face, searchResult, faceId);

            // 步骤4-6: 处理源文件关联和业务逻辑
            processSourceRelations(context, searchResult, faceId, isNew);

            // 步骤7: 异步生成拼图模板
            asyncGeneratePuzzleTemplate(context.face.getScenicId(), faceId, context.face.getMemberId(), scene);

            return searchResult;

        } catch (BaseException e) {
            throw e;
        } catch (Exception e) {
            log.error("人脸匹配流程异常，faceId={}", faceId, e);
            throw new BaseException("人脸匹配处理失败");
        }
    }

    /**
     * 步骤1: 准备匹配上下文
     * 获取人脸信息、景区配置、人脸识别适配器等必要数据
     */
    private MatchingContext prepareMatchingContext(Long faceId, boolean isNew) {
        FaceEntity face = faceRepository.getFace(faceId);
        if (face == null) {
            log.warn("人脸不存在，faceId: {}", faceId);
            return null;
        }

        // 人工选择的无需重新匹配（新用户除外）
        if (!isNew && Integer.valueOf(1).equals(face.getIsManual())) {
            log.info("人工选择的，无需匹配，faceId: {}", faceId);
            return null;
        }

        log.debug("开始人脸匹配：faceId={}, isNew={}", faceId, isNew);

        ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(face.getScenicId());
        IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(face.getScenicId());

        if (faceBodyAdapter == null) {
            log.error("无法获取人脸识别适配器，scenicId: {}", face.getScenicId());
            throw new BaseException("人脸识别服务不可用，请稍后再试");
        }

        MatchingContext context = new MatchingContext();
        context.face = face;
        context.scenicConfig = scenicConfig;
        context.faceBodyAdapter = faceBodyAdapter;
        return context;
    }

    /**
     * 步骤2: 执行人脸识别
     * 调用人脸识别服务进行人脸搜索
     */
    private SearchFaceRespVo executeFaceRecognition(MatchingContext context) {
        try {
            return taskFaceService.searchFace(
                    context.faceBodyAdapter,
                    String.valueOf(context.face.getScenicId()),
                    context.face.getFaceUrl(),
                    "人脸识别");
        } catch (Exception e) {
            log.error("人脸识别服务调用失败，faceId={}, scenicId={}",
                    context.face.getId(), context.face.getScenicId(), e);
            throw new BaseException("人脸识别失败，请换一张试试把~");
        }
    }

    /**
     * 步骤3: 更新人脸结果
     * 保存人脸匹配结果到数据库
     */
    private void updateFaceResult(FaceEntity originalFace, SearchFaceRespVo searchResult, Long faceId) {
        try {
            FaceEntity faceEntity = new FaceEntity();
            faceEntity.setId(faceId);
            faceEntity.setScore(searchResult.getScore());
            faceEntity.setMatchResult(searchResult.getSearchResultJson());

            if (searchResult.getFirstMatchRate() != null) {
                faceEntity.setFirstMatchRate(BigDecimal.valueOf(searchResult.getFirstMatchRate()));
            }

            if (searchResult.getSampleListIds() != null) {
                faceEntity.setMatchSampleIds(searchResult.getSampleListIds().stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(",")));
            }

            faceEntity.setCreateAt(new Date());
            faceEntity.setScenicId(originalFace.getScenicId());
            faceEntity.setMemberId(originalFace.getMemberId());
            faceEntity.setFaceUrl(originalFace.getFaceUrl());

            faceMapper.update(faceEntity);
            faceRepository.clearFaceCache(faceEntity.getId());

            log.debug("人脸结果更新成功：faceId={}, score={}, sampleCount={}",
                    faceId, searchResult.getScore(),
                    searchResult.getSampleListIds() != null ? searchResult.getSampleListIds().size() : 0);
        } catch (Exception e) {
            log.error("更新人脸结果失败，faceId={}", faceId, e);
            throw new BaseException("保存人脸识别结果失败");
        }
    }

    /**
     * 步骤4-6: 处理源文件关联和业务逻辑
     * 包括：源文件关联、免费逻辑、购买状态、视频重切、任务创建、数据持久化
     */
    private void processSourceRelations(MatchingContext context, SearchFaceRespVo searchResult,
                                        Long faceId, boolean isNew) {
        List<Long> sampleListIds = searchResult.getSampleListIds();

        if (sampleListIds == null || sampleListIds.isEmpty()) {
            log.warn("人脸匹配无结果：faceId={}", faceId);

            // 无匹配结果时，将切片状态设置为已完成，避免前端一直显示"合成中"
            faceStatusManager.setFaceCutStatus(faceId, FaceCutStatus.COMPLETED);

            // 记录低阈值检测
            if (searchResult.isLowThreshold()) {
                metricsRecorder.recordLowThreshold(faceId);
                log.debug("触发低阈值检测，记录faceId: {}", faceId);
            }
            return;
        }

        try {
            // 4. 源文件关联：处理匹配到的源文件
            List<MemberSourceEntity> memberSourceEntityList =
                    sourceRelationProcessor.processMemberSources(sampleListIds, context.face);

            if (memberSourceEntityList.isEmpty()) {
                log.warn("未找到有效的源文件，faceId={}", faceId);
                return;
            }

            // 5. 业务逻辑处理
            processBusinessLogic(context, memberSourceEntityList, faceId, sampleListIds, isNew);

            // 6. 数据持久化
            persistSourceRelations(faceId, memberSourceEntityList);

            // 7. 任务创建
            createTaskIfNeeded(context.face.getScenicId(), faceId);

            log.info("人脸匹配完成：faceId={}, 匹配样本数={}, 关联源文件数={}",
                    faceId, sampleListIds.size(), memberSourceEntityList.size());

        } catch (Exception e) {
            log.error("处理源文件关联失败，faceId={}", faceId, e);
            // 源文件关联失败不影响主流程，记录错误但不抛出异常
        }
    }

    /**
     * 步骤5: 业务逻辑处理
     * 处理免费逻辑、购买状态、视频重切
     */
    private void processBusinessLogic(MatchingContext context, List<MemberSourceEntity> memberSourceEntityList,
                                      Long faceId, List<Long> sampleListIds, boolean isNew) {
        // 免费逻辑
        List<Long> freeSourceIds = sourceRelationProcessor.processFreeSourceLogic(
                memberSourceEntityList, context.face.getScenicId(), isNew);

        // 购买状态
        buyStatusProcessor.processBuyStatus(
                memberSourceEntityList, freeSourceIds,
                context.face.getMemberId(), context.face.getScenicId(), faceId);

        // 视频重切
        videoRecreationHandler.handleVideoRecreation(
                context.face.getScenicId(), memberSourceEntityList,
                faceId, context.face.getMemberId(), sampleListIds, isNew);
    }

    /**
     * 步骤6: 数据持久化
     * 过滤并保存源文件关联关系
     */
    private void persistSourceRelations(Long faceId, List<MemberSourceEntity> memberSourceEntityList) {
        // 过滤已存在的关联关系和无效的source引用
        List<MemberSourceEntity> existingFiltered = sourceMapper.filterExistingRelations(memberSourceEntityList);
        List<MemberSourceEntity> validFiltered = sourceMapper.filterValidSourceRelations(existingFiltered);

        if (!validFiltered.isEmpty()) {
            sourceMapper.addRelations(validFiltered);
            log.debug("创建关联关系: faceId={}, 原始数量={}, 过滤后数量={}",
                    faceId, memberSourceEntityList.size(), validFiltered.size());
        } else {
            log.warn("没有有效的关联关系可创建: faceId={}, 原始数量={}",
                    faceId, memberSourceEntityList.size());
        }

        // 清除缓存
        memberRelationRepository.clearSCacheByFace(faceId);
        faceStatusManager.invalidatePuzzleSourceVersion(faceId);
    }

    /**
     * 步骤7: 任务创建
     * 根据配置决定是否自动创建任务
     */
    private void createTaskIfNeeded(Long scenicId, Long faceId) {
        if (!scenicConfigFacade.isFaceSelectFirst(scenicId)) {
            taskService.autoCreateTaskByFaceId(faceId);
        } else {
            log.debug("景区配置 face_select_first=true，跳过自动创建任务：faceId={}", faceId);
            faceStatusManager.setFaceCutStatus(faceId, FaceCutStatus.WAITING_USER_SELECT);
        }
    }

    /**
     * 步骤8: 异步生成拼图模板
     * 在人脸匹配完成后，异步为该景区的所有启用的拼图模板生成图片
     *
     * @return
     */
    private void asyncGeneratePuzzleTemplate(Long scenicId, Long faceId, Long memberId, String scene) {
        // 查询该景区所有启用状态的拼图模板
        List<PuzzleTemplateEntity> templateList = puzzleRepository.listTemplateByScenic(scenicId); // 查询启用状态的模板

        if (templateList == null || templateList.isEmpty()) {
            log.debug("景区不存在启用的拼图模板，跳过生成: scenicId={}", scenicId);
            return;
        }

        log.info("景区存在 {} 个启用的拼图模板，开始逐个生成: scenicId={}", templateList.size(), scenicId);

        // 获取人脸信息用于动态数据
        FaceEntity face = faceRepository.getFace(faceId);
        if (face == null) {
            log.warn("人脸信息不存在，无法生成拼图: faceId={}", faceId);
            return;
        }
        ScenicV2DTO scenicBasic = scenicRepository.getScenicBasic(face.getScenicId());

        // 准备公共动态数据
        Map<String, String> baseDynamicData = new HashMap<>();
        if (face.getFaceUrl() != null) {
            baseDynamicData.put("faceImage", face.getFaceUrl());
            baseDynamicData.put("userAvatar", face.getFaceUrl());
        }
        baseDynamicData.put("faceId", String.valueOf(faceId));
        baseDynamicData.put("scenicName", scenicBasic.getName());
        baseDynamicData.put("scenicText", scenicBasic.getName());
        Date maxCreateTime = sourceMapper.getMaxCreateTimeByFaceId(faceId);
        baseDynamicData.put("dateStr", DateUtil.format(
                maxCreateTime != null ? maxCreateTime : new Date(), "yyyy.MM.dd"));

        templateList
            .forEach(template -> {
                    log.info("开始生成拼图: scenicId={}, templateCode={}, templateName={}",
                            scenicId, template.getCode(), template.getName());

                    // 构建生成请求
                    PuzzleGenerateRequest generateRequest = new PuzzleGenerateRequest();
                    generateRequest.setScenicId(scenicId);
                    generateRequest.setUserId(memberId);
                    generateRequest.setFaceId(faceId);
                    generateRequest.setBusinessType("face_matching");
                    generateRequest.setTemplateCode(template.getCode());
                    generateRequest.setOutputFormat("JPEG");
                    generateRequest.setQuality(80);
                    generateRequest.setDynamicData(new HashMap<>(baseDynamicData));
                    generateRequest.setRequireRuleMatch(true);
                    try {
                        if (template.getAutoAddPrint() > 0 && Strings.CI.equals(scene, "printer")) {
                            puzzleGenerateService.generateSync(generateRequest);
                        } else {
                            puzzleGenerateService.generateAsync(generateRequest);
                        }

                    } catch (Exception e) {
                        // 异步任务失败不影响主流程，仅记录日志
                        log.error("异步生成拼图模板失败: scenicId={}, faceId={}, e={}", scenicId, faceId, e.getMessage());
                    }
                });
    }

    /**
     * 关联样本扩展
     * 根据 face_sample_association 表，将同组样本ID加入匹配结果
     */
    private void expandSampleAssociation(SearchFaceRespVo searchResult, Long scenicId, Long faceId) {
        List<Long> sampleListIds = searchResult.getSampleListIds();
        if (sampleListIds == null || sampleListIds.isEmpty()) {
            return;
        }
        try {
            List<Long> associatedIds = faceSampleAssociationMapper.findAssociatedSampleIds(scenicId, sampleListIds);
            if (associatedIds == null || associatedIds.isEmpty()) {
                return;
            }

            Set<Long> currentSet = new HashSet<>(sampleListIds);
            List<Long> netNewIds = associatedIds.stream()
                    .filter(id -> !currentSet.contains(id))
                    .distinct()
                    .collect(Collectors.toList());

            if (!netNewIds.isEmpty()) {
                List<Long> expandedIds = new ArrayList<>(sampleListIds);
                expandedIds.addAll(netNewIds);
                searchResult.setSampleListIds(expandedIds);
                log.info("关联样本扩展完成: faceId={}, scenicId={}, 原始数={}, 新增数={}, 扩展后={}",
                        faceId, scenicId, sampleListIds.size(), netNewIds.size(), expandedIds.size());
            }
        } catch (Exception e) {
            log.error("关联样本扩展失败, faceId={}, scenicId={}", faceId, scenicId, e);
            // 扩展失败不影响主流程
        }
    }

    /**
     * 匹配上下文
     * 封装匹配过程中需要的所有上下文信息
     */
    private static class MatchingContext {
        FaceEntity face;
        ScenicConfigManager scenicConfig;
        IFaceBodyAdapter faceBodyAdapter;
    }
}
