package com.qqchen.deploy.backend.deploy.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.deploy.dto.JenkinsSyncHistoryDTO;
import com.qqchen.deploy.backend.deploy.entity.*;
import com.qqchen.deploy.backend.deploy.dto.JenkinsBuildDTO;
import com.qqchen.deploy.backend.deploy.enums.ExternalSystemSyncStatus;
import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType;
import com.qqchen.deploy.backend.deploy.integration.response.*;
import com.qqchen.deploy.backend.deploy.query.JenkinsBuildQuery;
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
import com.qqchen.deploy.backend.deploy.repository.*;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService;
import com.qqchen.deploy.backend.deploy.dto.sync.JenkinsSyncContext;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, JenkinsBuildDTO, JenkinsBuildQuery, Long>
    implements IJenkinsBuildService {

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private IJenkinsViewRepository jenkinsViewRepository;

    @Resource
    private IJenkinsJobRepository jenkinsJobRepository;

    @Resource
    private IJenkinsBuildRepository jenkinsBuildRepository;

    @Resource
    private IJenkinsServiceIntegration jenkinsServiceIntegration;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private IJenkinsSyncHistoryService jenkinsSyncHistoryService;

    @Resource(name = "jenkinsTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    @Transactional
    public Integer syncAllBuilds(Long externalSystemId) {
        // 1. 创建同步上下文
        JenkinsSyncContext context = createSyncContext(externalSystemId);
        
        try {
            // 2. 执行同步
            return doSync(context);
        } catch (Exception e) {
            // 3. 处理异常
            handleSyncException(context, e);
            throw e;
        }
    }

    private JenkinsSyncContext createSyncContext(Long externalSystemId) {
        // 1. 查询外部系统
        ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
            .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));

        // 2. 创建同步历史
        JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO();
        syncHistory.setExternalSystemId(externalSystemId);
        syncHistory.setSyncType(JenkinsSyncType.BUILD);
        syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING);
        jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);

        // 3. 返回同步上下文
        return new JenkinsSyncContext(externalSystem, syncHistory);
    }

    private Integer doSync(JenkinsSyncContext context) {
        // 1. 获取所有视图
        List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(context.getExternalSystem().getId());
        if (views.isEmpty()) {
            log.info("No views found for external system: {}", context.getExternalSystem().getId());
            updateSyncHistorySuccess(context.getSyncHistory());
            return 0;
        }

        // 2. 并发同步每个视图
        List<CompletableFuture<Integer>> futures = views.stream()
            .map(view -> CompletableFuture.supplyAsync(
                () -> syncView(context.getExternalSystem(), view),
                threadPoolTaskExecutor
            ))
            .collect(Collectors.toList());

        // 3. 等待所有任务完成并汇总结果
        int totalSyncedBuilds = futures.stream()
            .map(future -> {
                try {
                    return future.get(5, TimeUnit.MINUTES);
                } catch (Exception e) {
                    log.error("Failed to get sync result", e);
                    return 0;
                }
            })
            .mapToInt(Integer::intValue)
            .sum();

        // 4. 更新同步历史
        updateSyncHistorySuccess(context.getSyncHistory());
        
        log.info("Successfully synchronized total {} builds for external system: {}",
            totalSyncedBuilds, context.getExternalSystem().getId());
        return totalSyncedBuilds;
    }

    @Transactional
    protected Integer syncView(ExternalSystem externalSystem, JenkinsView view) {
        try {
            // 1. 获取视图下的所有任务
            List<JenkinsJob> jobs = jenkinsJobRepository.findByExternalSystemIdAndViewId(
                externalSystem.getId(), view.getId());
            
            if (jobs.isEmpty()) {
                return 0;
            }

            // 2. 一次性获取视图下所有任务的最新状态
            List<JenkinsJobResponse> jobResponses = jenkinsServiceIntegration.listJobs(externalSystem, view.getViewName());
            if (jobResponses.isEmpty()) {
                log.info("No job information available for view: {}", view.getViewName());
                return 0;
            }

            // 3. 创建任务名称到响应的映射
            Map<String, JenkinsJobResponse> jobResponseMap = jobResponses.stream()
                .collect(Collectors.toMap(JenkinsJobResponse::getName, job -> job));

            // 4. 同步每个任务的构建信息
            return jobs.stream()
                .map(job -> {
                    JenkinsJobResponse jobResponse = jobResponseMap.get(job.getJobName());
                    return syncJob(externalSystem, job, jobResponse);
                })
                .mapToInt(Integer::intValue)
                .sum();
        } catch (Exception e) {
            log.error("Failed to sync view: {}", view.getViewName(), e);
            return 0;
        }
    }

    @Transactional
    protected Integer syncJob(ExternalSystem externalSystem, JenkinsJob job, JenkinsJobResponse jobResponse) {
        try {
            if (jobResponse == null || jobResponse.getLastBuild() == null) {
                log.info("No build information available for job: {}", job.getJobName());
                return 0;
            }

            // 1. 获取数据库中最后一次构建记录
            Optional<JenkinsBuild> lastBuild = jenkinsBuildRepository.findTopByExternalSystemIdAndJobIdOrderByBuildNumberDesc(
                externalSystem.getId(), job.getId());

            // 2. 获取需要同步的构建信息
            List<JenkinsBuildResponse> newBuilds = getNewBuilds(externalSystem, job, jobResponse, lastBuild);
            if (newBuilds.isEmpty()) {
                log.info("No new builds to sync for job: {}", job.getJobName());
                return 0;
            }

            // 3. 保存新的构建信息
            saveNewBuilds(externalSystem, job, newBuilds);
            
            // 4. 更新任务的最新构建信息
            updateJobLastBuild(job, jobResponse);

            log.info("Successfully synchronized {} builds for job: {}", newBuilds.size(), job.getJobName());
            return newBuilds.size();
        } catch (Exception e) {
            log.error("Failed to sync job: {}", job.getJobName(), e);
            return 0;
        }
    }

    private List<JenkinsBuildResponse> getNewBuilds(
            ExternalSystem externalSystem, 
            JenkinsJob job,
            JenkinsJobResponse jobResponse,
            Optional<JenkinsBuild> lastBuild) {
        
        // 1. 获取最新构建号（从jobResponse中获取，避免额外的API调用）
        if (jobResponse.getLastBuild() == null) {
            log.info("No builds found for job: {}", job.getJobName());
            return Collections.emptyList();
        }
        Integer latestBuildNumber = jobResponse.getLastBuild().getNumber();

        // 2. 确定同步范围
        Integer fromBuildNumber;
        if (lastBuild.isEmpty()) {
            // 首次同步，从第一个构建开始
            log.info("First time sync for job: {}, will sync all builds", job.getJobName());
            fromBuildNumber = 1;
        } else {
            // 增量同步，从上次同步的下一个构建开始
            fromBuildNumber = lastBuild.get().getBuildNumber() + 1;
        }

        // 3. 如果没有新的构建，直接返回
        if (fromBuildNumber > latestBuildNumber) {
            log.info("No new builds to sync for job: {} (last build: {}, latest build: {})",
                job.getJobName(), lastBuild.map(JenkinsBuild::getBuildNumber).orElse(null), latestBuildNumber);
            return Collections.emptyList();
        }

        // 4. 获取构建信息
        List<JenkinsBuildResponse> builds = jenkinsServiceIntegration.listBuilds(externalSystem, job.getJobName());
        
        // 5. 过滤出需要的构建
        List<JenkinsBuildResponse> newBuilds = builds.stream()
            .filter(build -> build.getNumber() >= fromBuildNumber && build.getNumber() <= latestBuildNumber)
            .collect(Collectors.toList());

        log.info("Found {} new builds for job: {} (from build: {}, to build: {})",
            newBuilds.size(), job.getJobName(), fromBuildNumber, latestBuildNumber);
        return newBuilds;
    }

    private void saveNewBuilds(
            ExternalSystem externalSystem,
            JenkinsJob job,
            List<JenkinsBuildResponse> builds) {
        List<JenkinsBuild> jenkinsBuilds = builds.stream()
            .map(buildResponse -> {
                JenkinsBuild build = new JenkinsBuild();
                build.setExternalSystemId(externalSystem.getId());
                build.setJobId(job.getId());
                build.setBuildNumber(buildResponse.getNumber());
                updateBuildFromResponse(build, buildResponse);
                return build;
            })
            .collect(Collectors.toList());

        log.info("Saving {} builds for job: {}", jenkinsBuilds.size(), job.getJobName());
        jenkinsBuildRepository.saveAll(jenkinsBuilds);
    }

    private void updateJobLastBuild(JenkinsJob job, JenkinsJobResponse jobResponse) {
        job.setLastBuildNumber(jobResponse.getLastBuild().getNumber());
        if (jobResponse.getLastBuild().getTimestamp() != null) {
            job.setLastBuildTime(LocalDateTime.ofInstant(
                Instant.ofEpochMilli(jobResponse.getLastBuild().getTimestamp()),
                ZoneId.systemDefault()
            ));
        }
        jenkinsJobRepository.save(job);
    }

    private void handleSyncException(JenkinsSyncContext context, Exception e) {
        log.error("Failed to sync Jenkins builds for external system: {}", 
            context.getExternalSystem().getId(), e);
        
        context.getSyncHistory().setStatus(ExternalSystemSyncStatus.FAILED);
        context.getSyncHistory().setErrorMessage(e.getMessage());
        jenkinsSyncHistoryService.saveOrUpdateHistory(context.getSyncHistory());
    }

    private void updateSyncHistorySuccess(JenkinsSyncHistoryDTO syncHistory) {
        syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
        jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
    }

    private void updateBuildFromResponse(JenkinsBuild jenkinsBuild, JenkinsBuildResponse response) {
        jenkinsBuild.setBuildUrl(response.getUrl());
        jenkinsBuild.setBuildStatus(response.getResult());
        jenkinsBuild.setDuration(response.getDuration());

        if (response.getTimestamp() != null) {
            LocalDateTime startTime = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(response.getTimestamp()),
                ZoneId.systemDefault()
            );
            jenkinsBuild.setStarttime(startTime);
        }

        try {
            String actionsJson = objectMapper.writeValueAsString(response.getActions());
            jenkinsBuild.setActions(actionsJson);
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize build actions", e);
            jenkinsBuild.setActions("{}");
        }
    }

    @Override
    public Long countByExternalSystemId(Long externalSystemId) {
        QJenkinsBuild qJenkinsBuild = QJenkinsBuild.jenkinsBuild;
        return super.repository.count(
            qJenkinsBuild.externalSystemId.eq(externalSystemId)
                .and(qJenkinsBuild.deleted.eq(false))
        );
    }
} 