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

import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.RepositoryProject;
import com.qqchen.deploy.backend.deploy.entity.RepositoryBranch;
import com.qqchen.deploy.backend.deploy.dto.RepositoryBranchDTO;
import com.qqchen.deploy.backend.deploy.enums.ExternalSystemSyncStatus;
import com.qqchen.deploy.backend.deploy.enums.RepositorySyncType;
import com.qqchen.deploy.backend.deploy.integration.IGitServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.GitBranchResponse;
import com.qqchen.deploy.backend.deploy.query.RepositoryBranchQuery;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositoryProjectRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositoryBranchRepository;
import com.qqchen.deploy.backend.deploy.service.IRepositoryBranchService;
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 com.qqchen.deploy.backend.deploy.dto.RepositorySyncHistoryDTO;
import com.qqchen.deploy.backend.deploy.service.IRepositorySyncHistoryService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;


import java.util.Objects;

/**
 * Git仓库分支服务实现
 */
@Slf4j
@Service
public class RepositoryBranchServiceImpl extends BaseServiceImpl<RepositoryBranch, RepositoryBranchDTO, RepositoryBranchQuery, Long>
    implements IRepositoryBranchService {

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private IRepositoryProjectRepository repositoryProjectRepository;

    @Resource
    private IRepositoryBranchRepository repositoryBranchRepository;

    @Resource
    private IGitServiceIntegration gitServiceIntegration;

    @Resource
    private IRepositorySyncHistoryService repositorySyncHistoryService;

    @Resource(name = "repositoryBranchExecutor")
    private ThreadPoolTaskExecutor executor;

    private static final int BATCH_SIZE = 100;

    private static final int MAX_RETRIES = 3;

    private static final long RETRY_DELAY = 100L;

    @Transactional
    public void saveBatch(List<RepositoryBranch> branches) {
        int retryCount = 0;
        while (retryCount < MAX_RETRIES) {
            try {
                for (int i = 0; i < branches.size(); i += BATCH_SIZE) {
                    int end = Math.min(i + BATCH_SIZE, branches.size());
                    List<RepositoryBranch> batch = branches.subList(i, end);
                    repositoryBranchRepository.saveAll(batch);
                }
                return;
            } catch (ObjectOptimisticLockingFailureException e) {
                retryCount++;
                if (retryCount >= MAX_RETRIES) {
                    throw e;
                }
                log.warn("Optimistic locking failure during batch save, retry attempt {}", retryCount);
                try {
                    Thread.sleep(RETRY_DELAY * retryCount);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Interrupted during retry delay", ie);
                }
            }
        }
    }

    @Override
    @Transactional
    public Integer syncBranches(Long externalSystemId) {
        // 1. 创建同步历史记录
        RepositorySyncHistoryDTO syncHistory = repositorySyncHistoryService.createSyncHistory(externalSystemId, RepositorySyncType.BRANCH);

        try {
            // 2. 获取外部系统信息
            ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
                .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
            log.info("Starting branch sync for external system: {} (ID: {})", externalSystem.getName(), externalSystemId);

            // 3. 获取所有项目
            List<RepositoryProject> projects = repositoryProjectRepository.findByExternalSystemIdAndDeletedFalse(externalSystemId);
            if (projects.isEmpty()) {
                log.info("No projects found for external system: {}", externalSystem.getName());
                repositorySyncHistoryService.updateSyncHistory(syncHistory.getId(), ExternalSystemSyncStatus.SUCCESS, null);
                return 0;
            }
            log.info("Found {} projects to sync", projects.size());

            // 4. 一次性获取所有现有分支
            List<RepositoryBranch> existingBranches = repositoryBranchRepository.findByExternalSystemIdAndDeletedFalse(externalSystemId);
            Map<String, Map<Long, RepositoryBranch>> existingBranchMap = existingBranches.stream()
                .collect(Collectors.groupingBy(
                    RepositoryBranch::getName,
                    Collectors.toMap(
                        RepositoryBranch::getProjectId,
                        Function.identity(),
                        (existing, replacement) -> existing
                    )
                ));
            log.info("Found {} existing branches in database", existingBranches.size());

            AtomicInteger totalSyncedBranches = new AtomicInteger(0);
            List<CompletableFuture<List<RepositoryBranch>>> futures = new ArrayList<>();

            // 5. 并行处理每个项目
            for (RepositoryProject project : projects) {
                CompletableFuture<List<RepositoryBranch>> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        log.info("Syncing branches for project: {} (ID: {}, GitLab ID: {})", project.getName(), project.getId(), project.getRepoProjectId());

                        // 5.1 获取当前项目在GitLab上的所有分支
                        List<GitBranchResponse> remoteBranches = gitServiceIntegration.branches(externalSystem, project.getRepoProjectId());
                        log.info("Found {} remote branches for project: {}", remoteBranches.size(), project.getName());

                        List<RepositoryBranch> branchesToUpdate = new ArrayList<>();
                        Set<String> processedBranches = new HashSet<>();

                        // 5.2 处理远程分支
                        for (GitBranchResponse remoteBranch : remoteBranches) {
                            Map<Long, RepositoryBranch> projectBranches = existingBranchMap.getOrDefault(remoteBranch.getName(), new HashMap<>());
                            RepositoryBranch branch = projectBranches.getOrDefault(project.getId(), new RepositoryBranch());

                            // 检查分支是否有更新
                            if (isBranchChanged(branch, remoteBranch)) {
                                // 更新分支信息
                                updateBranchInfo(branch, externalSystemId, project.getId(), project.getRepoProjectId(), remoteBranch);
                                branchesToUpdate.add(branch);
                                log.debug("Branch {} has changes, will be updated", remoteBranch.getName());
                            } else {
                                log.debug("Branch {} has no changes, skipping update", remoteBranch.getName());
                            }

                            processedBranches.add(remoteBranch.getName());
                            totalSyncedBranches.incrementAndGet();
                        }

                        // 5.3 处理已删除的分支
                        Map<String, RepositoryBranch> projectBranches = existingBranchMap.values().stream()
                            .flatMap(map -> map.values().stream())
                            .filter(b -> b.getProjectId().equals(project.getId()))
                            .collect(Collectors.toMap(RepositoryBranch::getName, Function.identity()));

                        for (RepositoryBranch branch : projectBranches.values()) {
                            if (!processedBranches.contains(branch.getName())) {
                                branch.setDeleted(true);
                                branchesToUpdate.add(branch);
                                totalSyncedBranches.incrementAndGet();
                            }
                        }

                        log.info("Processed {} branches for project: {}", branchesToUpdate.size(), project.getName());
                        return branchesToUpdate;
                    } catch (Exception e) {
                        log.error("Failed to sync branches for project: {} (ID: {}, GitLab ID: {})", project.getName(), project.getId(), project.getRepoProjectId(), e);
                        throw new RuntimeException(e);
                    }
                }, executor);

                futures.add(future);
            }

            // 6. 等待所有异步任务完成并收集结果
            List<RepositoryBranch> allBranchesToSave = futures.stream()
                .map(CompletableFuture::join)
                .flatMap(List::stream)
                .collect(Collectors.toList());

            // 7. 批量保存所有分支
            if (!allBranchesToSave.isEmpty()) {
                try {
                    log.info("Saving {} branches to database in batch", allBranchesToSave.size());
                    saveBatch(allBranchesToSave);
                } catch (Exception e) {
                    log.error("Error saving branches in batch", e);
                    throw e;
                }
            }

            // 8. 更新同步历史为成功
            repositorySyncHistoryService.updateSyncHistory(syncHistory.getId(), ExternalSystemSyncStatus.SUCCESS, null);

            log.info("Successfully synchronized {} branches for external system: {}",
                totalSyncedBranches.get(), externalSystem.getName());
            return totalSyncedBranches.get();
        } catch (Exception e) {
            // 9. 更新同步历史为失败
            log.error("Failed to sync branches", e);
            repositorySyncHistoryService.updateSyncHistory(syncHistory.getId(), ExternalSystemSyncStatus.FAILED, e.getMessage());
            throw new BusinessException(ResponseCode.REPOSITORY_BRANCH_SYNC_FAILED);
        }
    }

    /**
     * 检查分支信息是否有更新
     */
    private boolean isBranchChanged(RepositoryBranch branch, GitBranchResponse remoteBranch) {
        if (branch.getId() == null) {
            return true; // 新分支
        }

        return !Objects.equals(generateCompareKey(branch), generateCompareKey(remoteBranch));
    }

    /**
     * 生成用于比较的字符串
     * 使用StringBuilder拼接所有需要比较的字段，提高性能
     */
    private String generateCompareKey(Object obj) {
        StringBuilder key = new StringBuilder();
        if (obj instanceof RepositoryBranch branch) {
            key.append(branch.getLastCommitId())
                .append(branch.getIsDefaultBranch())
                .append(branch.getCanPush())
                .append(branch.getDevelopersCanPush())
                .append(branch.getDevelopersCanMerge())
                .append(branch.getWebUrl());
        } else if (obj instanceof GitBranchResponse branch) {
            key.append(branch.getCommitId())
                .append(branch.getIsDefaultBranch())
                .append(branch.getCanPush())
                .append(branch.getDevelopersCanPush())
                .append(branch.getDevelopersCanMerge())
                .append(branch.getWebUrl());
        }
        return key.toString();
    }

    /**
     * 更新分支信息
     */
    private void updateBranchInfo(RepositoryBranch branch, Long externalSystemId, Long projectId, Long gitProjectId, GitBranchResponse remoteBranch) {
        branch.setExternalSystemId(externalSystemId);
        branch.setProjectId(projectId);
        branch.setRepoProjectId(gitProjectId);
        branch.setName(remoteBranch.getName());
        branch.setIsDefaultBranch(remoteBranch.getIsDefaultBranch());
        branch.setCanPush(remoteBranch.getCanPush());
        branch.setDevelopersCanPush(remoteBranch.getDevelopersCanPush());
        branch.setDevelopersCanMerge(remoteBranch.getDevelopersCanMerge());
        branch.setLastCommitId(remoteBranch.getCommitId());
        branch.setCommitMessage(remoteBranch.getCommitMessage());
        branch.setCommitAuthor(remoteBranch.getCommitAuthor());
        branch.setCommitDate(remoteBranch.getCommitDate());
        branch.setWebUrl(remoteBranch.getWebUrl());
        branch.setLastUpdateTime(remoteBranch.getLastUpdateTime());
        branch.setLastCommitTime(remoteBranch.getLastCommitTime());
    }

    @Override
    public Long countByExternalSystemId(Long externalSystemId) {
        return repositoryBranchRepository.countByExternalSystemId(externalSystemId);
    }

    @Override
    public Long countByProjectId(Long projectId) {
        return repositoryBranchRepository.countByProjectId(projectId);
    }
}