package com.ycwl.basic.biz;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.ycwl.basic.enums.FaceCutStatus;
import com.ycwl.basic.enums.FacePieceUpdateStatus;
import com.ycwl.basic.enums.TemplateRenderStatus;
import com.ycwl.basic.mapper.TaskMapper;
import com.ycwl.basic.mapper.task.TaskRenderJobMappingMapper;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.task.entity.TaskRenderJobMappingEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 人脸状态缓存管理器
 * 统一管理人脸相关的内存缓存状态（使用Caffeine）
 */
@Slf4j
@Component
public class FaceStatusManager {

    /**
     * 默认过期时间：1小时
     */
    private static final long DEFAULT_EXPIRE_SECONDS = 3600L;

    /**
     * 人脸切片状态缓存
     */
    private final Cache<String, Integer> faceCutStatusCache;

    /**
     * 人脸片段更新状态缓存（全局和模板级）
     * 键存在=无新片段，键不存在=有新片段
     */
    private final Cache<String, Boolean> faceNoPieceUpdateCache;

    /**
     * 人脸模板渲染状态缓存
     */
    private final Cache<String, Integer> templateRenderCache;

    /**
     * 拼图素材版本缓存
     * 键：faceId:puzzleTemplateId -> 当时的图片源数量
     * 用于判断拼图模板的素材是否发生变化，避免重复生成
     */
    private final Cache<String, Integer> puzzleSourceVersionCache;

    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private TaskRenderJobMappingMapper taskRenderJobMappingMapper;

    public FaceStatusManager() {
        // 初始化三个独立的缓存实例
        this.faceCutStatusCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
                .maximumSize(10000)
                .build();

        this.faceNoPieceUpdateCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
                .maximumSize(10000)
                .build();

        this.templateRenderCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
                .maximumSize(10000)
                .build();

        this.puzzleSourceVersionCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
                .maximumSize(10000)
                .build();
    }

    // ==================== 切片状态相关方法 ====================

    /**
     * 设置人脸切片状态
     * @param faceId 人脸ID
     * @param status 切片状态
     */
    public void setFaceCutStatus(Long faceId, FaceCutStatus status) {
        if (faceId == null || status == null) {
            log.warn("设置切片状态参数为空: faceId={}, status={}", faceId, status);
            return;
        }
        faceCutStatusCache.put(String.valueOf(faceId), status.getCode());
        log.debug("设置切片状态: faceId={}, status={}", faceId, status.getDescription());
    }

    /**
     * 获取人脸切片状态
     * @param faceId 人脸ID
     * @return 切片状态，缓存不存在时返回 COMPLETED（已完成）
     */
    public FaceCutStatus getFaceCutStatus(Long faceId) {
        if (faceId == null) {
            log.warn("获取切片状态参数为空: faceId={}", faceId);
            return FaceCutStatus.COMPLETED;
        }
        Integer code = faceCutStatusCache.getIfPresent(String.valueOf(faceId));
        if (code == null) {
            log.debug("切片状态缓存不存在，返回默认值COMPLETED: faceId={}", faceId);
            return FaceCutStatus.COMPLETED;
        }
        return FaceCutStatus.fromCodeOrDefault(code, FaceCutStatus.COMPLETED);
    }

    /**
     * 删除人脸切片状态缓存
     * @param faceId 人脸ID
     */
    public void deleteFaceCutStatus(Long faceId) {
        if (faceId == null) {
            return;
        }
        faceCutStatusCache.invalidate(String.valueOf(faceId));
        log.debug("删除切片状态缓存: faceId={}", faceId);
    }

    // ==================== 片段更新状态相关方法 ====================

    /**
     * 标记无新片段（设置缓存键）
     * @param faceId 人脸ID
     * @param templateId 模板ID（可选，为null时标记全局状态）
     */
    public void markNoNewPieces(Long faceId, Long templateId) {
        if (faceId == null) {
            log.warn("标记无新片段参数为空: faceId={}", faceId);
            return;
        }

        if (templateId == null) {
            // 全局标记：该人脸的所有模板都无新片段
            faceNoPieceUpdateCache.put(String.valueOf(faceId), Boolean.TRUE);
            log.debug("标记无新片段（全局）: faceId={}", faceId);
        } else {
            // 模板级标记：该人脸在该模板下无新片段
            faceNoPieceUpdateCache.put(faceId + ":" + templateId, Boolean.TRUE);
            log.debug("标记无新片段（模板）: faceId={}, templateId={}", faceId, templateId);
        }
    }

    /**
     * 标记有新片段（删除缓存键）
     * @param faceId 人脸ID
     * @param templateId 模板ID（可选，为null时标记全局状态）
     */
    public void markHasNewPieces(Long faceId, Long templateId) {
        if (faceId == null) {
            log.warn("标记有新片段参数为空: faceId={}", faceId);
            return;
        }

        if (templateId == null) {
            // 全局标记：该人脸有新片段
            faceNoPieceUpdateCache.invalidate(String.valueOf(faceId));
            log.debug("标记有新片段（全局）: faceId={}", faceId);
        } else {
            // 模板级标记：该人脸在该模板下有新片段
            faceNoPieceUpdateCache.invalidate(faceId + ":" + templateId);
            log.debug("标记有新片段（模板）: faceId={}, templateId={}", faceId, templateId);
        }
    }

    /**
     * 获取人脸片段更新状态
     * @param faceId 人脸ID
     * @param templateId 模板ID（可选，为null时查询全局状态）
     * @return 片段更新状态，键存在=无新片段，键不存在=有新片段
     */
    public FacePieceUpdateStatus getFacePieceUpdateStatus(Long faceId, Long templateId) {
        if (faceId == null) {
            log.warn("获取片段更新状态参数为空: faceId={}", faceId);
            return FacePieceUpdateStatus.HAS_NEW_PIECES;
        }

        String key = templateId == null ? String.valueOf(faceId) : faceId + ":" + templateId;
        boolean exists = faceNoPieceUpdateCache.getIfPresent(key) != null;
        FacePieceUpdateStatus status = FacePieceUpdateStatus.fromKeyExists(exists);

        if (templateId == null) {
            log.debug("获取片段更新状态（全局）: faceId={}, status={}", faceId, status.getDescription());
        } else {
            log.debug("获取片段更新状态（模板）: faceId={}, templateId={}, status={}",
                    faceId, templateId, status.getDescription());
        }

        return status;
    }

    /**
     * 获取人脸片段更新状态 - 全局版本
     * @param faceId 人脸ID
     * @return 片段更新状态，键存在=无新片段，键不存在=有新片段
     */
    public FacePieceUpdateStatus getFacePieceUpdateStatus(Long faceId) {
        return getFacePieceUpdateStatus(faceId, null);
    }

    /**
     * 判断是否有新片段
     * @param faceId 人脸ID
     * @param templateId 模板ID（可选，为null时查询全局状态）
     * @return true=有新片段，false=无新片段；如果templateId为null则默认返回true（有新片段）
     */
    public boolean hasNewPieces(Long faceId, Long templateId) {
        if (templateId == null) {
            // 如果没有指定templateId，默认认为有新片段
            log.debug("未指定templateId，默认返回有新片段: faceId={}", faceId);
            return true;
        }
        return getFacePieceUpdateStatus(faceId, templateId).hasNewPieces();
    }

    /**
     * 判断是否有新片段 - 全局版本
     * @param faceId 人脸ID
     * @return true=有新片段，false=无新片段
     */
    public boolean hasNewPieces(Long faceId) {
        return getFacePieceUpdateStatus(faceId, null).hasNewPieces();
    }

    // ==================== 模板渲染状态相关方法 ====================

    /**
     * 设置人脸模板渲染状态
     * @param faceId 人脸ID
     * @param templateId 模板ID
     * @param status 渲染状态
     */
    public void setTemplateRenderStatus(Long faceId, Long templateId, TemplateRenderStatus status) {
        if (faceId == null || templateId == null || status == null) {
            log.warn("设置模板渲染状态参数为空: faceId={}, templateId={}, status={}", faceId, templateId, status);
            return;
        }
        templateRenderCache.put(faceId + ":" + templateId, status.getCode());
        log.debug("设置模板渲染状态: faceId={}, templateId={}, status={}", faceId, templateId, status.getDescription());
    }

    /**
     * 获取人脸模板渲染状态
     * @param faceId 人脸ID
     * @param templateId 模板ID
     * @return 渲染状态，缓存不存在时返回 null
     */
    public TemplateRenderStatus getTemplateRenderStatus(Long faceId, Long templateId) {
        if (faceId == null || templateId == null) {
            log.warn("获取模板渲染状态参数为空: faceId={}, templateId={}", faceId, templateId);
            return null;
        }
        Integer code = templateRenderCache.getIfPresent(faceId + ":" + templateId);
        if (code == null) {
            log.debug("模板渲染状态缓存不存在: faceId={}, templateId={}", faceId, templateId);
            // 查询数据库：通过 task_render_job_mapping 确定渲染状态
            TaskEntity task = taskMapper.listLastFaceTemplateTask(faceId, templateId);
            if (task == null) {
                setTemplateRenderStatus(faceId, templateId, TemplateRenderStatus.NONE);
                return TemplateRenderStatus.NONE;
            }
            TaskRenderJobMappingEntity mapping = taskRenderJobMappingMapper.selectByTaskId(task.getId());
            if (mapping == null) {
                setTemplateRenderStatus(faceId, templateId, TemplateRenderStatus.NONE);
                return TemplateRenderStatus.NONE;
            }
            TemplateRenderStatus status = switch (mapping.getRenderStatus()) {
                case TaskRenderJobMappingEntity.STATUS_PENDING -> TemplateRenderStatus.RENDERING;
                case TaskRenderJobMappingEntity.STATUS_PREVIEW_READY,
                     TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING,
                     TaskRenderJobMappingEntity.STATUS_COMPLETED -> TemplateRenderStatus.RENDERED;
                default -> TemplateRenderStatus.NONE; // FAILED 等异常状态
            };
            setTemplateRenderStatus(faceId, templateId, status);
            return status;
        }
        return TemplateRenderStatus.fromCode(code);
    }

    /**
     * 删除人脸模板渲染状态缓存
     * @param faceId 人脸ID
     * @param templateId 模板ID
     */
    public void deleteTemplateRenderStatus(Long faceId, Long templateId) {
        if (faceId == null || templateId == null) {
            return;
        }
        templateRenderCache.invalidate(faceId + ":" + templateId);
        log.debug("删除模板渲染状态缓存: faceId={}, templateId={}", faceId, templateId);
    }

    /**
     * 删除人脸的所有模板渲染状态（使用模式匹配）
     * 注意：此操作可能影响性能，谨慎使用
     * @param faceId 人脸ID
     */
    public void deleteAllTemplateRenderStatus(Long faceId) {
        if (faceId == null) {
            return;
        }
        String prefix = faceId + ":";
        long count = templateRenderCache.asMap().keySet().stream()
                .filter(key -> key.startsWith(prefix))
                .peek(templateRenderCache::invalidate)
                .count();
        if (count > 0) {
            log.debug("批量删除模板渲染状态缓存: faceId={}, count={}", faceId, count);
        }
    }

    // ==================== 拼图素材版本相关方法 ====================

    /**
     * 标记拼图素材版本（记录当前的图片源数量）
     * 在拼图生成成功后调用，用于后续判断素材是否变化
     *
     * @param faceId           人脸ID
     * @param puzzleTemplateId 拼图模板ID（全局唯一）
     * @param sourceCount      当前的图片源数量
     */
    public void markPuzzleSourceVersion(Long faceId, Long puzzleTemplateId, int sourceCount) {
        if (faceId == null || puzzleTemplateId == null) {
            log.warn("标记拼图素材版本参数为空: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
            return;
        }
        String key = faceId + ":" + puzzleTemplateId;
        puzzleSourceVersionCache.put(key, sourceCount);
        log.debug("标记拼图素材版本: faceId={}, puzzleTemplateId={}, sourceCount={}", faceId, puzzleTemplateId, sourceCount);
    }

    /**
     * 判断拼图素材是否发生变化
     * 通过比较当前的图片源数量与缓存中记录的数量
     *
     * @param faceId             人脸ID
     * @param puzzleTemplateId   拼图模板ID（全局唯一）
     * @param currentSourceCount 当前的图片源数量
     * @return true=素材已变化（需要重新生成），false=素材未变化（可以跳过生成）
     */
    public boolean isPuzzleSourceChanged(Long faceId, Long puzzleTemplateId, int currentSourceCount) {
        if (faceId == null || puzzleTemplateId == null) {
            log.warn("判断拼图素材变化参数为空: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
            return true; // 参数不合法时默认认为有变化
        }

        String key = faceId + ":" + puzzleTemplateId;
        Integer cachedCount = puzzleSourceVersionCache.getIfPresent(key);

        if (cachedCount == null) {
            // 缓存不存在，认为有变化（首次生成或缓存过期）
            log.debug("拼图素材版本缓存不存在，需要生成: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
            return true;
        }

        boolean changed = !cachedCount.equals(currentSourceCount);
        if (changed) {
            log.debug("拼图素材已变化: faceId={}, puzzleTemplateId={}, cachedCount={}, currentCount={}",
                    faceId, puzzleTemplateId, cachedCount, currentSourceCount);
        } else {
            log.debug("拼图素材未变化，可跳过生成: faceId={}, puzzleTemplateId={}, sourceCount={}",
                    faceId, puzzleTemplateId, currentSourceCount);
        }

        return changed;
    }

    /**
     * 使指定人脸的所有拼图素材版本缓存失效
     * 当人脸的图片关联发生变化时调用（如人脸匹配后新增了关联）
     *
     * @param faceId 人脸ID
     */
    public void invalidatePuzzleSourceVersion(Long faceId) {
        if (faceId == null) {
            return;
        }
        String prefix = faceId + ":";
        long count = puzzleSourceVersionCache.asMap().keySet().stream()
                .filter(key -> key.startsWith(prefix))
                .peek(puzzleSourceVersionCache::invalidate)
                .count();
        if (count > 0) {
            log.debug("批量使拼图素材版本缓存失效: faceId={}, count={}", faceId, count);
        }
    }
}
