package com.ycwl.basic.service.task;

import com.ycwl.basic.biz.FaceStatusManager;
import com.ycwl.basic.enums.TemplateRenderStatus;
import com.ycwl.basic.integration.render.dto.job.FinalizeMP4Response;
import com.ycwl.basic.integration.render.dto.job.JobStatusResponse;
import com.ycwl.basic.integration.render.service.RenderJobIntegrationService;
import com.ycwl.basic.mapper.TaskMapper;
import com.ycwl.basic.mapper.VideoMapper;
import com.ycwl.basic.mapper.task.TaskRenderJobMappingMapper;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
import com.ycwl.basic.model.task.entity.TaskRenderJobMappingEntity;
import com.ycwl.basic.repository.MemberRelationRepository;
import com.ycwl.basic.repository.VideoRepository;
import com.ycwl.basic.repository.VideoTaskRepository;
import com.ycwl.basic.utils.SnowFlakeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 渲染作业轮询服务
 * 定时查询zt-render-worker服务中的渲染作业状态，并更新本地task状态
 *
 * 状态流转：
 * PENDING → PREVIEW_READY → MP4_COMPOSING → COMPLETED
 *    │            │              │              │
 *    └────────────┴──────────────┴──────────────┴──→ FAILED
 */
@Slf4j
@Service
@EnableScheduling
@RequiredArgsConstructor
@Profile({"prod"}) // 开发和生产环境启用
public class RenderJobPollingService {

    private final TaskRenderJobMappingMapper mappingMapper;
    private final RenderJobIntegrationService renderJobService;
    private final TaskMapper taskMapper;
    private final VideoMapper videoMapper;
    private final VideoTaskRepository videoTaskRepository;
    private final VideoRepository videoRepository;
    private final MemberRelationRepository memberRelationRepository;
    private final FaceStatusManager faceStatusManager;

    /**
     * 定时轮询间隔：1+1=2秒
     */
    private static final int POLL_INTERVAL_SECONDS = 1;

    /**
     * 每次查询的最大记录数
     */
    private static final int BATCH_SIZE = 50;

    /**
     * 定时轮询渲染作业状态
     * 每1秒执行一次
     */
    @Scheduled(fixedDelay = 1000)
    public void pollRenderJobs() {
        try {
            log.debug("[渲染轮询] 开始轮询渲染作业状态");

            // 查询待轮询的记录（包含MP4_COMPOSING状态）
            List<String> pendingStatuses = Arrays.asList(
                    TaskRenderJobMappingEntity.STATUS_PENDING,
                    TaskRenderJobMappingEntity.STATUS_PREVIEW_READY,
                    TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING
            );

            List<TaskRenderJobMappingEntity> mappings = mappingMapper.selectPendingForPolling(
                    pendingStatuses,
                    POLL_INTERVAL_SECONDS,
                    BATCH_SIZE
            );

            if (mappings.isEmpty()) {
                log.debug("[渲染轮询] 无待处理记录");
                return;
            }

            log.info("[渲染轮询] 查询到 {} 条待处理记录", mappings.size());

            // 处理每条记录
            for (TaskRenderJobMappingEntity mapping : mappings) {
                try {
                    processMapping(mapping);
                } catch (Exception e) {
                    log.error("[渲染轮询] 处理失败, mappingId: {}, taskId: {}, renderJobId: {}, error: {}",
                            mapping.getId(), mapping.getTaskId(), mapping.getRenderJobId(), e.getMessage(), e);
                    handleProcessError(mapping, e);
                }
            }

            log.debug("[渲染轮询] 轮询完成");

        } catch (Exception e) {
            log.error("[渲染轮询] 轮询异常", e);
        }
    }

    /**
     * 处理单条mapping记录
     */
    @Transactional(rollbackFor = Exception.class)
    public void processMapping(TaskRenderJobMappingEntity mapping) {
        Long renderJobId = mapping.getRenderJobId();
        Long taskId = mapping.getTaskId();
        String currentStatus = mapping.getRenderStatus();

        log.debug("[渲染轮询] 处理记录: mappingId={}, taskId={}, renderJobId={}, currentStatus={}",
                mapping.getId(), taskId, renderJobId, currentStatus);

        // 查询渲染作业状态
        JobStatusResponse jobStatus;
        try {
            jobStatus = renderJobService.getJobStatus(renderJobId);
        } catch (Exception e) {
            log.warn("[渲染轮询] 查询作业状态失败, renderJobId: {}, error: {}", renderJobId, e.getMessage());
            // 注：此处不调用incrementRetryCount，因为@Transactional会回滚
            // 外层handleProcessError会负责增加重试次数
            throw e;
        }

        // 检查作业状态
        String status = jobStatus.getStatus();
        Integer publishedCount = jobStatus.getPublishedCount();
        Integer segmentCount = jobStatus.getSegmentCount();
        String playUrl = jobStatus.getPlayUrl();
        String mp4Url = jobStatus.getMp4Url();

        log.info("[渲染轮询] 作业状态: taskId={}, status={}, publishedCount={}/{}, playUrl={}, mp4Url={}",
                taskId, status, publishedCount, segmentCount, playUrl, mp4Url);

        // 处理失败状态
        if ("FAILED".equals(status) || "CANCELED".equals(status)) {
            handleJobFailed(mapping, jobStatus);
            return;
        }

        // 状态流转处理
        switch (currentStatus) {
            case TaskRenderJobMappingEntity.STATUS_PENDING:
                handlePendingStatus(mapping, jobStatus, taskId);
                break;
            case TaskRenderJobMappingEntity.STATUS_PREVIEW_READY:
                handlePreviewReadyStatus(mapping, jobStatus, taskId);
                break;
            case TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING:
                handleMp4ComposingStatus(mapping, jobStatus, taskId);
                break;
            default:
                log.warn("[渲染轮询] 未知状态: {}", currentStatus);
        }
    }

    /**
     * 处理PENDING状态
     * PENDING → PREVIEW_READY：当publishedCount >= 2时
     */
    private void handlePendingStatus(TaskRenderJobMappingEntity mapping, JobStatusResponse jobStatus, Long taskId) {
        Integer publishedCount = jobStatus.getPublishedCount();
        Integer segmentCount = jobStatus.getSegmentCount();
        String playUrl = jobStatus.getPlayUrl();

        if (publishedCount != null && publishedCount >= TaskRenderJobMappingEntity.MIN_PUBLISHED_FOR_PREVIEW) {
            log.info("[渲染轮询] 预览就绪: taskId={}, publishedCount={}/{}, playUrl={}",
                    taskId, publishedCount, segmentCount, playUrl);

            // 更新mapping状态为PREVIEW_READY
            updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_PREVIEW_READY,
                    publishedCount, segmentCount, playUrl, null);

            // 更新模板渲染状态缓存为已渲染（预览就绪即视为渲染成功）
            updateTemplateRenderStatus(taskId, TemplateRenderStatus.RENDERED);

            // 更新task的videoUrl为预览地址
            if (StringUtils.isNotBlank(playUrl)) {
                TaskEntity task = new TaskEntity();
                task.setId(taskId);
                task.setVideoUrl(playUrl);
                task.setStatus(1); // 设置为完成状态
                taskMapper.update(task);
                videoTaskRepository.clearTaskCache(taskId);
                log.info("[渲染轮询] 已更新task预览URL和状态: taskId={}, playUrl={}, status=1", taskId, playUrl);

                // 处理video记录（类似taskSuccess逻辑）
                try {
                    handleVideoRecordForPreview(taskId, playUrl);
                } catch (Exception e) {
                    log.warn("[渲染轮询] 处理video记录失败: taskId={}, error={}", taskId, e.getMessage(), e);
                }

                // 异步发送视频生成通知（仅记录日志，实际通知可能需要在MP4完成后）
                log.info("[渲染轮询] 预览视频已就绪，可发送通知: taskId={}", taskId);
            }
        } else {
            // 更新片段信息
            updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_PENDING,
                    publishedCount, segmentCount, null, null);
        }
    }

    /**
     * 处理PREVIEW_READY状态
     * PREVIEW_READY → MP4_COMPOSING：当所有片段都已发布时，调用finalize-mp4接口
     */
    private void handlePreviewReadyStatus(TaskRenderJobMappingEntity mapping, JobStatusResponse jobStatus, Long taskId) {
        Integer publishedCount = jobStatus.getPublishedCount();
        Integer segmentCount = jobStatus.getSegmentCount();
        String playUrl = jobStatus.getPlayUrl();
        Long renderJobId = mapping.getRenderJobId();

        // 检查是否所有片段都已发布
        if (publishedCount != null && segmentCount != null && publishedCount.equals(segmentCount) && segmentCount > 0) {
            log.info("[渲染轮询] 所有片段已发布，开始创建MP4合成任务: taskId={}, renderJobId={}, publishedCount={}/{}",
                    taskId, renderJobId, publishedCount, segmentCount);

            try {
                // 调用finalize-mp4接口创建MP4合成任务
                FinalizeMP4Response response = renderJobService.createFinalizeMP4Task(renderJobId);
                log.info("[渲染轮询] MP4合成任务创建成功: taskId={}, renderJobId={}, mp4TaskId={}, status={}",
                        taskId, renderJobId, response.getTaskId(), response.getStatus());

                // 更新mapping状态为MP4_COMPOSING
                updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING,
                        publishedCount, segmentCount, playUrl, null);

            } catch (Exception e) {
                // 409表示任务已存在，直接进入MP4_COMPOSING状态
                if (e.getMessage() != null && e.getMessage().contains("409")) {
                    log.info("[渲染轮询] MP4合成任务已存在，继续等待: taskId={}, renderJobId={}", taskId, renderJobId);
                    updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING,
                            publishedCount, segmentCount, playUrl, null);
                } else {
                    log.warn("[渲染轮询] 创建MP4合成任务失败: taskId={}, renderJobId={}, error={}",
                            taskId, renderJobId, e.getMessage());
                    // 不改变状态，下次轮询重试
                    updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_PREVIEW_READY,
                            publishedCount, segmentCount, playUrl, null);
                }
            }
        } else {
            // 更新片段信息
            updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_PREVIEW_READY,
                    publishedCount, segmentCount, playUrl, null);
        }
    }

    /**
     * 处理MP4_COMPOSING状态
     * MP4_COMPOSING → COMPLETED：当mp4Url有值时
     */
    private void handleMp4ComposingStatus(TaskRenderJobMappingEntity mapping, JobStatusResponse jobStatus, Long taskId) {
        Integer publishedCount = jobStatus.getPublishedCount();
        Integer segmentCount = jobStatus.getSegmentCount();
        String playUrl = jobStatus.getPlayUrl();
        String mp4Url = jobStatus.getMp4Url();

        if (StringUtils.isNotBlank(mp4Url)) {
            log.info("[渲染轮询] MP4合成完成: taskId={}, mp4Url={}", taskId, mp4Url);

            // 更新mapping状态为COMPLETED
            updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_COMPLETED,
                    publishedCount, segmentCount, playUrl, mp4Url);

            // 更新task的videoUrl为最终MP4地址
            TaskEntity task = new TaskEntity();
            task.setId(taskId);
            task.setVideoUrl(mp4Url);
            taskMapper.update(task);
            videoTaskRepository.clearTaskCache(taskId);
            log.info("[渲染轮询] 已更新task最终MP4 URL: taskId={}, mp4Url={}", taskId, mp4Url);

            // 更新video记录的videoUrl为最终MP4地址
            try {
                handleVideoRecordForMP4(taskId, mp4Url);
            } catch (Exception e) {
                log.warn("[渲染轮询] 更新video的MP4 URL失败: taskId={}, error={}", taskId, e.getMessage(), e);
            }
        } else {
            // MP4还在合成中，更新片段信息
            log.debug("[渲染轮询] MP4合成中: taskId={}, 等待下次轮询", taskId);
            updateMappingStatus(mapping.getId(), TaskRenderJobMappingEntity.STATUS_MP4_COMPOSING,
                    publishedCount, segmentCount, playUrl, null);
        }
    }

    /**
     * 处理作业失败
     */
    private void handleJobFailed(TaskRenderJobMappingEntity mapping, JobStatusResponse jobStatus) {
        String errorCode = jobStatus.getErrorCode();
        String errorMessage = jobStatus.getErrorMessage();

        log.warn("[渲染轮询] 作业失败: taskId={}, status={}, errorCode={}, errorMessage={}",
                mapping.getTaskId(), jobStatus.getStatus(), errorCode, errorMessage);

        mappingMapper.updateToFailed(
                mapping.getId(),
                errorCode,
                errorMessage,
                new Date()
        );

        // 渲染失败，重置模板渲染状态
        updateTemplateRenderStatus(mapping.getTaskId(), TemplateRenderStatus.NONE);
    }

    /**
     * 更新模板渲染状态缓存
     * 根据 taskId 查询关联的 faceId 和 templateId，更新 FaceStatusManager 中的渲染状态
     */
    private void updateTemplateRenderStatus(Long taskId, TemplateRenderStatus status) {
        try {
            var taskInfo = taskMapper.getById(taskId);
            if (taskInfo != null && taskInfo.getFaceId() != null && taskInfo.getTemplateId() != null) {
                faceStatusManager.setTemplateRenderStatus(taskInfo.getFaceId(), taskInfo.getTemplateId(), status);
                log.info("[渲染轮询] 已更新模板渲染状态: taskId={}, faceId={}, templateId={}, status={}",
                        taskId, taskInfo.getFaceId(), taskInfo.getTemplateId(), status.getDescription());
            }
        } catch (Exception e) {
            log.warn("[渲染轮询] 更新模板渲染状态缓存失败: taskId={}, error={}", taskId, e.getMessage());
        }
    }

    /**
     * 更新mapping状态
     */
    private void updateMappingStatus(Long id, String renderStatus, Integer publishedCount,
                                      Integer segmentCount, String previewUrl, String mp4Url) {
        mappingMapper.updateRenderStatus(
                id,
                renderStatus,
                publishedCount,
                segmentCount,
                previewUrl,
                mp4Url,
                new Date()
        );
    }

    /**
     * 处理异常
     */
    private void handleProcessError(TaskRenderJobMappingEntity mapping, Exception e) {
        try {
            mappingMapper.incrementRetryCount(mapping.getId());

            // 超过最大重试次数，标记为失败
            if (mapping.getRetryCount() != null &&
                    mapping.getRetryCount() >= TaskRenderJobMappingEntity.MAX_RETRY_COUNT - 1) {
                mappingMapper.updateToFailed(
                        mapping.getId(),
                        "MAX_RETRY",
                        "超过最大重试次数: " + e.getMessage(),
                        new Date()
                );
            }
        } catch (Exception ex) {
            log.error("[渲染轮询] 处理错误失败", ex);
        }
    }

    /**
     * 处理video记录（预览就绪时）
     * 类似taskSuccess的逻辑，但简化版本
     */
    private void handleVideoRecordForPreview(Long taskId, String videoUrl) {
        try {
            var taskResp = taskMapper.getById(taskId);
            if (taskResp == null) {
                log.warn("[渲染轮询] task不存在: taskId={}", taskId);
                return;
            }

            VideoEntity video = videoMapper.findByTaskId(taskId);
            if (video != null) {
                // 更新已有video记录
                video.setVideoUrl(videoUrl);
                videoMapper.update(video);
                videoRepository.clearVideoCache(video.getId());
                log.info("[渲染轮询] 已更新video预览URL: taskId={}, videoId={}, videoUrl={}",
                        taskId, video.getId(), videoUrl);
            } else {
                // 创建新video记录
                video = new VideoEntity();
                video.setId(SnowFlakeUtil.getLongId());
                video.setScenicId(taskResp.getScenicId());
                video.setTemplateId(taskResp.getTemplateId());
                video.setTaskId(taskId);
                video.setFaceId(taskResp.getFaceId());
                video.setVideoUrl(videoUrl);
                video.setCreateTime(new Date());
                videoMapper.add(video);
                log.info("[渲染轮询] 已创建video预览记录: taskId={}, videoId={}, videoUrl={}",
                        taskId, video.getId(), videoUrl);
            }

            // 更新member_video关联表（isBuy=0，预览阶段未购买）
            videoMapper.updateRelationWhenTaskSuccess(taskId, video.getId(), 0);
            memberRelationRepository.clearVCacheByFace(taskResp.getFaceId());
            log.info("[渲染轮询] 已更新member_video关联: taskId={}, videoId={}", taskId, video.getId());

        } catch (Exception e) {
            log.error("[渲染轮询] 处理video记录失败: taskId={}", taskId, e);
            throw e;
        }
    }

    /**
     * 更新video记录的MP4地址（MP4合成完成时）
     */
    private void handleVideoRecordForMP4(Long taskId, String mp4Url) {
        try {
            VideoEntity video = videoMapper.findByTaskId(taskId);
            if (video != null) {
                video.setVideoUrl(mp4Url);
                videoMapper.update(video);
                videoRepository.clearVideoCache(video.getId());
                log.info("[渲染轮询] 已更新video最终MP4 URL: taskId={}, videoId={}, mp4Url={}",
                        taskId, video.getId(), mp4Url);
            } else {
                log.warn("[渲染轮询] video不存在，无法更新MP4 URL: taskId={}", taskId);
            }
        } catch (Exception e) {
            log.error("[渲染轮询] 更新video的MP4 URL失败: taskId={}", taskId, e);
            throw e;
        }
    }
}
