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

import com.qqchen.deploy.backend.deploy.converter.RepositoryProjectConverter;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.RepositoryGroup;
import com.qqchen.deploy.backend.deploy.entity.RepositoryProject;
import com.qqchen.deploy.backend.deploy.dto.RepositoryProjectDTO;
import com.qqchen.deploy.backend.deploy.integration.IGitServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.GitProjectResponse;
import com.qqchen.deploy.backend.deploy.query.RepositoryProjectQuery;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositoryGroupRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositoryProjectRepository;
import com.qqchen.deploy.backend.deploy.service.IRepositoryProjectService;
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.orm.ObjectOptimisticLockingFailureException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Set;
import java.util.ArrayList;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Collections;

/**
 * Git仓库项目服务实现
 */
@Slf4j
@Service
public class RepositoryProjectServiceImpl extends BaseServiceImpl<RepositoryProject, RepositoryProjectDTO, RepositoryProjectQuery, Long> implements IRepositoryProjectService {

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private IRepositoryGroupRepository repositoryGroupRepository;

    @Resource
    private IRepositoryProjectRepository repositoryProjectRepository;

    @Resource
    private IGitServiceIntegration gitServiceIntegration;

    @Resource
    private RepositoryProjectConverter repositoryProjectConverter;

    @Resource(name = "repositoryProjectExecutor")
    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<RepositoryProject> projects) {
        int retryCount = 0;
        while (retryCount < MAX_RETRIES) {
            try {
                for (int i = 0; i < projects.size(); i += BATCH_SIZE) {
                    int end = Math.min(i + BATCH_SIZE, projects.size());
                    List<RepositoryProject> batch = projects.subList(i, end);
                    repositoryProjectRepository.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 syncProjects(Long externalSystemId) {
        try {
            // 1. 获取外部系统信息
            ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
                .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
            log.info("Start syncing projects for external system: {} (ID: {})", externalSystem.getName(), externalSystemId);

            // 2. 获取所有组
            List<RepositoryGroup> groups = repositoryGroupRepository.findByExternalSystemIdAndDeletedFalse(externalSystemId);
            if (groups.isEmpty()) {
                log.info("No groups found for external system: {}", externalSystem.getName());
                return 0;
            }
            log.info("Found {} groups to sync", groups.size());

            // 3. 获取现有项目映射（包括已删除的）
            Map<Long, RepositoryProject> existingProjects = Collections.synchronizedMap(
                repositoryProjectRepository
                    .findByExternalSystemId(externalSystemId)
                    .stream()
                    .collect(Collectors.toMap(
                        RepositoryProject::getRepoProjectId,
                        Function.identity(),
                        (existing, replacement) -> {
                            log.warn("Duplicate project found with ID: {}, path: {}", existing.getRepoProjectId(), existing.getPathWithNamespace());
                            return existing;
                        }
                    ))
            );

            // 4. 用于跟踪已处理的项目ID
            Set<Long> processedProjectIds = ConcurrentHashMap.newKeySet();
            AtomicInteger totalCount = new AtomicInteger(0);
            List<CompletableFuture<Void>> futures = new ArrayList<>();

            // 使用线程安全的列表来存储待保存的项目
            List<RepositoryProject> projectsToSave = Collections.synchronizedList(new ArrayList<>());

            // 5. 遍历每个组，异步同步项目
            for (RepositoryGroup group : groups) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        List<GitProjectResponse> projectResponses = gitServiceIntegration.projectsByGroup(externalSystem, group.getRepoGroupId());
                        log.info("Processing group: {} (ID: {}), found {} projects", group.getName(), group.getRepoGroupId(), projectResponses.size());

                        for (GitProjectResponse projectResponse : projectResponses) {
                            if (!processedProjectIds.add(projectResponse.getId())) {
                                log.info("Project already processed: {} (ID: {}) in group: {}", projectResponse.getPathWithNamespace(), projectResponse.getId(), group.getName());
                                continue;
                            }

                            RepositoryProject project = existingProjects.get(projectResponse.getId());
                            if (project == null) {
                                project = new RepositoryProject();
                                project.setExternalSystemId(externalSystemId);
                                project.setRepoProjectId(projectResponse.getId());
                                log.info("Creating new project: {} (ID: {})",
                                    projectResponse.getPathWithNamespace(), projectResponse.getId());
                                totalCount.incrementAndGet();
                            } else {
                                log.debug("Updating existing project: {} (ID: {})",
                                    projectResponse.getPathWithNamespace(), projectResponse.getId());
                            }

                            project.setDeleted(false);
                            project.setName(projectResponse.getName());
                            project.setPath(projectResponse.getPath());
                            project.setDescription(projectResponse.getDescription());
                            project.setVisibility(projectResponse.getVisibility());
                            project.setRepoGroupId(group.getRepoGroupId());
                            project.setDefaultBranch(projectResponse.getDefaultBranch());
                            project.setWebUrl(projectResponse.getWebUrl());
                            project.setSshUrl(projectResponse.getSshUrlToRepo());
                            project.setHttpUrl(projectResponse.getHttpUrlToRepo());
                            project.setLastActivityAt(projectResponse.getLastActivityAt());
                            project.setNameWithNamespace(projectResponse.getNameWithNamespace());
                            project.setPathWithNamespace(projectResponse.getPathWithNamespace());
                            project.setCreatedAt(projectResponse.getCreatedAt());

                            projectsToSave.add(project);
                            synchronized (existingProjects) {
                                existingProjects.remove(projectResponse.getId());
                            }
                        }
                    } catch (Exception e) {
                        log.error("Error processing group {}: {}", group.getId(), e.getMessage(), e);
                        throw new CompletionException(e);
                    }
                }, executor);

                futures.add(future);
            }

            // 等待所有异步任务完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            // 批量保存项目
            if (!projectsToSave.isEmpty()) {
                try {
                    saveBatch(projectsToSave);
                } catch (Exception e) {
                    log.error("Error saving projects in batch", e);
                    throw e;
                }
            }

            // 6. 删除不存在的项目
            if (!existingProjects.isEmpty()) {
                List<Long> toDeleteIds = existingProjects.values().stream()
                    .filter(p -> !p.getDeleted())
                    .map(RepositoryProject::getId)
                    .collect(Collectors.toList());
                if (!toDeleteIds.isEmpty()) {
                    log.info("Deleting {} projects that no longer exist", toDeleteIds.size());
                    repositoryProjectRepository.deleteAllById(toDeleteIds);
                }
            }

            log.info("Successfully synced projects. Added {} new projects, processed {} total projects",
                totalCount.get(), processedProjectIds.size());
            return totalCount.get();
        } catch (Exception e) {
            log.error("Failed to sync repository projects for external system: {}", externalSystemId, e);
            throw new BusinessException(ResponseCode.REPOSITORY_PROJECT_SYNC_FAILED);
        }
    }

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

    @Override
    public List<RepositoryProjectDTO> findByExternalSystemId(Long externalSystemId) {
        return repositoryProjectConverter.toDtoList(repositoryProjectRepository.findByExternalSystemId(externalSystemId));
    }
}