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.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsBuild;
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.deploy.dto.JenkinsBuildDTO;
import com.qqchen.deploy.backend.deploy.entity.QJenkinsBuild;
import com.qqchen.deploy.backend.deploy.enums.ExternalSystemSyncStatus;
import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsJobResponse;
import com.qqchen.deploy.backend.deploy.query.JenkinsBuildQuery;
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildResponse;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsBuildRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsJobRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService;
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.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Jenkins构建信息 Service实现
 */
@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. 创建同步历史记录
        JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO();
        syncHistory.setExternalSystemId(externalSystemId);
        syncHistory.setSyncType(JenkinsSyncType.BUILD);
        syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING);
        jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);

        try {
            // 2. 查询外部系统
            ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId).orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
            // 3. 查询所有视图
            List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(externalSystemId);
            if (views.isEmpty()) {
                log.info("No views found for external system: {}", externalSystemId);
                // 更新同步历史为成功
                syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
                jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
                return 0;
            }
            // 4. 同步每个视图下的构建信息
            int totalSyncedBuilds = 0;
            StringBuilder errorMessages = new StringBuilder();
            for (JenkinsView view : views) {
                try {
                    Integer syncedBuilds = syncBuildsByView(externalSystem, view);
                    totalSyncedBuilds += syncedBuilds;
                    log.info("Successfully synchronized {} builds for view: {}", syncedBuilds, view.getViewName());
                } catch (Exception e) {
                    String errorMessage = String.format("Failed to sync builds for view %s: %s", view.getViewName(), e.getMessage());
                    log.error(errorMessage, e);
                    errorMessages.append(errorMessage).append("\n");
                }
            }

            // 5. 更新同步历史状态
            if (errorMessages.length() > 0) {
                syncHistory.setStatus(ExternalSystemSyncStatus.FAILED);
                syncHistory.setErrorMessage(errorMessages.toString());
            } else {
                syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
            }
            jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);

            log.info("Successfully synchronized total {} builds for external system: {}",
                totalSyncedBuilds, externalSystemId);
            return totalSyncedBuilds;
        } catch (Exception e) {
            log.error("Failed to sync Jenkins builds for external system: {}", externalSystemId, e);
            // 更新同步历史为失败
            syncHistory.setStatus(ExternalSystemSyncStatus.FAILED);
            syncHistory.setErrorMessage(e.getMessage());
            jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
            throw e;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer syncBuildsByView(ExternalSystem externalSystem, JenkinsView view) {
        // 1. 查询视图下的所有任务
        List<JenkinsJob> jobs = jenkinsJobRepository.findByExternalSystemIdAndViewId(externalSystem.getId(), view.getId());
        if (jobs.isEmpty()) {
            log.info("No jobs found for view: {}", view.getId());
            return 0;
        }

        // 2. 使用线程池并发同步每个任务的构建信息
        List<CompletableFuture<Integer>> futures = jobs.stream()
            .map(job -> CompletableFuture.supplyAsync(() -> {
                try {
                    Integer syncedBuilds = syncBuilds(externalSystem, job);
                    log.info("Successfully synchronized {} builds for job: {}", syncedBuilds, job.getJobName());
                    return syncedBuilds;
                } catch (Exception e) {
                    log.error("Failed to sync builds for job: {}", job.getJobName(), e);
                    return 0;
                }
            }, 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();

        log.info("Successfully synchronized total {} builds for view: {}", totalSyncedBuilds, view.getViewName());
        return totalSyncedBuilds;
    }

    @Override
    @Transactional
    public Integer syncBuilds(ExternalSystem externalSystem, JenkinsJob job) {
        JenkinsJobResponse queryJob = jenkinsServiceIntegration.job(externalSystem, job.getJobName());
        if (queryJob == null || queryJob.getLastBuild() == null) {
            log.info("No builds found for job: {}", job.getJobName());
            return 0;
        }

        // Check if sync is needed
        if (job.getLastBuildNumber() != null && queryJob.getLastBuild().getNumber() <= job.getLastBuildNumber()) {
            log.info("No new builds to sync for job: {}", job.getJobName());
            return 0;
        }

        List<JenkinsBuildResponse> buildResponses = jenkinsServiceIntegration.listBuilds(externalSystem, job.getJobName());
        if (buildResponses.isEmpty()) {
            log.info("No builds found for job: {}", job.getJobName());
            return 0;
        }

        // Convert and save/update build data
        List<JenkinsBuild> jenkinsBuilds = new ArrayList<>();
        for (JenkinsBuildResponse buildResponse : buildResponses) {
            // Skip existing builds
            if (job.getLastBuildNumber() != null && buildResponse.getNumber() <= job.getLastBuildNumber()) {
                continue;
            }

            // Create new build
            JenkinsBuild jenkinsBuild = new JenkinsBuild();
            jenkinsBuild.setExternalSystemId(externalSystem.getId());
            jenkinsBuild.setJobId(job.getId());
            jenkinsBuild.setBuildNumber(buildResponse.getNumber());
            updateBuildFromResponse(jenkinsBuild, buildResponse);
            jenkinsBuilds.add(jenkinsBuild);
            log.debug("Creating new Jenkins build: {} for job: {}", jenkinsBuild.getBuildNumber(), job.getJobName());
        }

        // Batch save
        if (!jenkinsBuilds.isEmpty()) {
            jenkinsBuildRepository.saveAll(jenkinsBuilds);

            // Update job's last build info
            job.setLastBuildNumber(queryJob.getLastBuild().getNumber());
            if (queryJob.getLastBuild().getTimestamp() != null) {
                job.setLastBuildTime(LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(queryJob.getLastBuild().getTimestamp()),
                    ZoneId.systemDefault()
                ));
            }
            jenkinsJobRepository.save(job);

            log.info("Successfully synchronized {} builds for job: {}", jenkinsBuilds.size(), job.getJobName());
        }

        return jenkinsBuilds.size();
    }

    /**
     * 从API响应更新构建信息
     */
    private void updateBuildFromResponse(JenkinsBuild jenkinsBuild, JenkinsBuildResponse response) {
        jenkinsBuild.setBuildUrl(response.getUrl());
        jenkinsBuild.setBuildStatus(response.getResult());
        jenkinsBuild.setDuration(response.getDuration());

        // 转换时间戳为LocalDateTime
        if (response.getTimestamp() != null) {
            LocalDateTime startTime = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(response.getTimestamp()),
                ZoneId.systemDefault()
            );
            jenkinsBuild.setStarttime(startTime);
        }

        // 将构建参数转换为JSON字符串
        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)));
    }
} 