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.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
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 AsyncTaskExecutor executor;

    @Resource
    private com.qqchen.deploy.backend.deploy.lock.SyncLockManager syncLockManager;

    private static final int BATCH_SIZE = 50; // 减小批次大小，提高容错性

    private static final int MAX_RETRIES = 3;

    private static final long RETRY_DELAY = 100L;

    private static final int MAX_WEB_URL_LENGTH = 1000; // web_url 字段最大长度

    /**
     * 批量保存分支（优化版）
     * 1. 分批保存，减少单次事务大小
     * 2. 单条记录失败不影响其他记录
     * 3. 支持乐观锁重试
     */
    @Transactional
    public void saveBatch(List<RepositoryBranch> branches) {
        if (branches == null || branches.isEmpty()) {
            return;
        }

        log.info("开始批量保存 {} 条分支记录", branches.size());
        int successCount = 0;
        int failureCount = 0;
        List<String> failedBranches = new ArrayList<>();

        // 分批处理
        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);

            // 批次内逐条保存，避免一条失败导致整批失败
            for (RepositoryBranch branch : batch) {
                int retryCount = 0;
                boolean saved = false;

                while (retryCount <= MAX_RETRIES && !saved) {
                    try {
                        // 数据清洗：截断过长的字段
                        sanitizeBranchData(branch);

                        // 保存单条记录
                        repositoryBranchRepository.save(branch);
                        successCount++;
                        saved = true;

                        if (retryCount > 0) {
                            log.info("分支 {} 重试第 {} 次成功", branch.getName(), retryCount);
                        }
                    } catch (ObjectOptimisticLockingFailureException e) {
                        // 乐观锁冲突，重试
                        retryCount++;
                        if (retryCount > MAX_RETRIES) {
                            failureCount++;
                            String errorMsg = String.format("%s (乐观锁冲突)", branch.getName());
                            failedBranches.add(errorMsg);
                            log.error("分支 {} 保存失败，乐观锁冲突重试 {} 次后仍失败", branch.getName(), MAX_RETRIES, e);
                        } else {
                            log.warn("分支 {} 乐观锁冲突，进行第 {} 次重试", branch.getName(), retryCount);
                            try {
                                Thread.sleep(RETRY_DELAY * retryCount);
                            } catch (InterruptedException ie) {
                                Thread.currentThread().interrupt();
                                log.error("重试延迟被中断", ie);
                            }
                        }
                    } catch (Exception e) {
                        // 其他异常（如数据过长、约束冲突等），记录日志并跳过
                        failureCount++;
                        String errorMsg = String.format("%s (%s)", branch.getName(), e.getMessage());
                        failedBranches.add(errorMsg);
                        log.error("分支 {} 保存失败: {}, webUrl长度={}", 
                            branch.getName(), 
                            e.getMessage(),
                            branch.getWebUrl() != null ? branch.getWebUrl().length() : 0,
                            e);
                        break; // 非乐观锁异常不重试
                    }
                }
            }

            log.info("批次 [{}-{}] 保存完成，成功: {}, 失败: {}", 
                i, end - 1, successCount, failureCount);
        }

        // 汇总结果
        log.info("批量保存完成 - 总数: {}, 成功: {}, 失败: {}", 
            branches.size(), successCount, failureCount);

        if (!failedBranches.isEmpty()) {
            log.warn("保存失败的分支列表（前10条）: {}", 
                failedBranches.stream().limit(10).collect(Collectors.toList()));
        }

        // 如果全部失败，抛出异常
        if (failureCount == branches.size() && branches.size() > 0) {
            throw new BusinessException(ResponseCode.REPOSITORY_BRANCH_SYNC_FAILED, 
                new Object[]{"所有分支保存失败"});
        }
    }

    /**
     * 数据清洗：截断过长的字段，避免数据库插入失败
     */
    private void sanitizeBranchData(RepositoryBranch branch) {
        // 截断 web_url
        if (branch.getWebUrl() != null && branch.getWebUrl().length() > MAX_WEB_URL_LENGTH) {
            String original = branch.getWebUrl();
            branch.setWebUrl(original.substring(0, MAX_WEB_URL_LENGTH));
            log.warn("分支 {} 的 web_url 超长（{}字符），已截断至 {} 字符", 
                branch.getName(), original.length(), MAX_WEB_URL_LENGTH);
        }

        // 截断 commit_message（TEXT 类型通常足够大，但防御性处理）
        if (branch.getCommitMessage() != null && branch.getCommitMessage().length() > 5000) {
            branch.setCommitMessage(branch.getCommitMessage().substring(0, 5000) + "...");
        }

        // 截断 commit_author
        if (branch.getCommitAuthor() != null && branch.getCommitAuthor().length() > 100) {
            branch.setCommitAuthor(branch.getCommitAuthor().substring(0, 100));
        }
    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void syncBranches(Long externalSystemId) {
        // 尝试获取锁
        if (!syncLockManager.tryLock(externalSystemId)) {
            return;
        }

        try {
            doSyncBranches(externalSystemId, null, null);
        } finally {
            syncLockManager.unlock(externalSystemId);
        }
    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void syncBranches(Long externalSystemId, Long repoGroupId) {
        // 尝试获取锁
        if (!syncLockManager.tryLock(externalSystemId, repoGroupId)) {
            return;
        }

        try {
            doSyncBranches(externalSystemId, repoGroupId, null);
        } finally {
            syncLockManager.unlock(externalSystemId, repoGroupId);
        }
    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void syncBranches(Long externalSystemId, Long repoGroupId, Long repoProjectId) {
        // 尝试获取锁
        if (!syncLockManager.tryLock(externalSystemId, repoGroupId, repoProjectId)) {
            return;
        }

        try {
            doSyncBranches(externalSystemId, repoGroupId, repoProjectId);
        } finally {
            syncLockManager.unlock(externalSystemId, repoGroupId, repoProjectId);
        }
    }

    /**
     * 执行分支同步的核心方法
     *
     * @param externalSystemId 外部系统ID
     * @param repoGroupId 仓库组ID（可选）
     * @param repoProjectId 仓库项目ID（可选）
     */
    private void doSyncBranches(Long externalSystemId, Long repoGroupId, Long repoProjectId) {
        // 1. 创建同步历史记录
        RepositorySyncHistoryDTO syncHistory = repositorySyncHistoryService.createSyncHistory(externalSystemId, RepositorySyncType.BRANCH);

        try {
            // 2. 获取外部系统信息
            ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
                .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
            
            // 记录同步模式
            if (repoProjectId != null) {
                log.info("Starting branch sync for project (external system: {}, repoGroupId: {}, repoProjectId: {})", 
                    externalSystemId, repoGroupId, repoProjectId);
            } else if (repoGroupId != null) {
                log.info("Starting branch sync for group (external system: {}, repoGroupId: {})", 
                    externalSystemId, repoGroupId);
            } else {
                log.info("Starting full branch sync for external system: {} (ID: {})", 
                    externalSystem.getName(), externalSystemId);
            }

            // 3. 根据参数获取需要同步的项目列表
            List<RepositoryProject> projects = getProjectsToSync(externalSystemId, repoGroupId, repoProjectId);
            if (projects.isEmpty()) {
                log.info("No projects found to sync");
                repositorySyncHistoryService.updateSyncHistory(syncHistory.getId(), ExternalSystemSyncStatus.SUCCESS, null);
                return;
            }
            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.debug("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.debug("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());
                            }

                            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.debug("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());
            
        } 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);
        }
    }

    /**
     * 根据参数获取需要同步的项目列表
     *
     * @param externalSystemId 外部系统ID
     * @param repoGroupId 仓库组ID（可选）
     * @param repoProjectId 仓库项目ID（可选）
     * @return 项目列表
     */
    private List<RepositoryProject> getProjectsToSync(Long externalSystemId, Long repoGroupId, Long repoProjectId) {
        if (repoProjectId != null) {
            // 同步单个项目（repoProjectId是Git系统中的项目ID，不是数据库主键）
            RepositoryProject project = repositoryProjectRepository
                .findByRepoProjectIdAndExternalSystemIdAndDeletedFalse(repoProjectId, externalSystemId)
                .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND, 
                    new Object[]{"Git项目ID", repoProjectId + "（外部系统：" + externalSystemId + "）"}));
            
            // 验证项目属于指定的仓库组（如果指定了repoGroupId）
            if (repoGroupId != null && !project.getRepoGroupId().equals(repoGroupId)) {
                throw new BusinessException(ResponseCode.DATA_NOT_FOUND, 
                    new Object[]{"Git项目ID", repoProjectId + "（不属于仓库组：" + repoGroupId + "）"});
            }
            
            return List.of(project);
        } else if (repoGroupId != null) {
            // 同步仓库组下所有项目（repoGroupId是Git系统中的组ID）
            return repositoryProjectRepository.findByExternalSystemIdAndRepoGroupIdAndDeletedFalse(externalSystemId, repoGroupId);
        } else {
            // 全量同步
            return repositoryProjectRepository.findByExternalSystemIdAndDeletedFalse(externalSystemId);
        }
    }

    /**
     * 检查分支信息是否有更新
     */
    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());
    }

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

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