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

import com.qqchen.deploy.backend.deploy.converter.RepositoryProjectConverter;
import com.qqchen.deploy.backend.deploy.dto.RepositorySyncHistoryDTO;
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.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.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.repository.IRepositoryBranchRepository;
import com.qqchen.deploy.backend.deploy.service.IRepositoryProjectService;
import com.qqchen.deploy.backend.deploy.service.IRepositorySyncHistoryService;
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.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;

import java.time.ZoneId;
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 IRepositoryBranchRepository repositoryBranchRepository;

    @Resource
    private IGitServiceIntegration gitServiceIntegration;

    @Resource
    private RepositoryProjectConverter repositoryProjectConverter;

    @Resource(name = "repositoryProjectExecutor")
    private AsyncTaskExecutor executor;

    @Resource
    private IRepositorySyncHistoryService repositorySyncHistoryService;

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

    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
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void syncProjects(Long externalSystemId) {
        // 尝试获取锁
        if (!syncLockManager.tryLock(externalSystemId)) {
            return;
        }

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

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

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

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

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

            // 3. 获取需要同步的仓库组列表
            List<RepositoryGroup> groups = getGroupsToSync(externalSystemId, repoGroupId);
            if (groups.isEmpty()) {
                log.debug("No groups found for external system: {}", externalSystem.getName());
                repositorySyncHistoryService.updateSyncHistory(projectHistory.getId(), ExternalSystemSyncStatus.SUCCESS, null);
                return;
            }
            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();
                            }

                            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());
                            // 将OffsetDateTime转换为LocalDateTime
                            project.setLastActivityAt(projectResponse.getLastActivityAt() != null ?
                                projectResponse.getLastActivityAt().atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() : null);
                            project.setNameWithNamespace(projectResponse.getNameWithNamespace());
                            project.setPathWithNamespace(projectResponse.getPathWithNamespace());
                            // 将OffsetDateTime转换为LocalDateTime
                            project.setCreatedAt(projectResponse.getCreatedAt() != null ?
                                projectResponse.getCreatedAt().atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() : null);

                            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);
                }
            }

            // 7. 更新同步历史记录为成功
            repositorySyncHistoryService.updateSyncHistory(projectHistory.getId(), ExternalSystemSyncStatus.SUCCESS, null);
            
            log.info("Successfully synced projects. Added {} new projects, processed {} total projects",
                totalCount.get(), processedProjectIds.size());
            
        } catch (Exception e) {
            // 8. 更新同步历史记录为失败
            repositorySyncHistoryService.updateSyncHistory(projectHistory.getId(), ExternalSystemSyncStatus.FAILED, e.getMessage());
            log.error("Failed to sync repository projects for external system: {}", externalSystemId, e);
            throw new BusinessException(ResponseCode.REPOSITORY_PROJECT_SYNC_FAILED);
        }
    }

    /**
     * 根据参数获取需要同步的仓库组列表
     *
     * @param externalSystemId 外部系统ID
     * @param repoGroupId 仓库组ID（可选）
     * @return 仓库组列表
     */
    private List<RepositoryGroup> getGroupsToSync(Long externalSystemId, Long repoGroupId) {
        if (repoGroupId != null) {
            // 同步单个仓库组（repoGroupId是Git系统中的组ID，不是数据库主键）
            RepositoryGroup group = repositoryGroupRepository
                .findByRepoGroupIdAndExternalSystemIdAndDeletedFalse(repoGroupId, externalSystemId)
                .orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND, 
                    new Object[]{"Git仓库组ID", repoGroupId + "（外部系统：" + externalSystemId + "）"}));
            
            return List.of(group);
        } else {
            // 全量同步
            return repositoryGroupRepository.findByExternalSystemIdAndDeletedFalse(externalSystemId);
        }
    }

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

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

    @Override
    public Page<RepositoryProjectDTO> page(RepositoryProjectQuery query) {
        Page<RepositoryProjectDTO> page = super.page(query);
        fillExtendedFields(page.getContent());
        return new PageImpl<>(page.getContent(), page.getPageable(), page.getTotalElements());
    }

    @Override
    public List<RepositoryProjectDTO> findAll(RepositoryProjectQuery query) {
        List<RepositoryProjectDTO> list = super.findAll(query);
        fillExtendedFields(list);
        return list;
    }

    /**
     * 批量填充扩展字段：repoGroupName、branchCount
     * <p>使用批量查询避免N+1问题
     */
    private void fillExtendedFields(List<RepositoryProjectDTO> projects) {
        if (projects.isEmpty()) {
            return;
        }

        // 1. 收集所有仓库组的 repo_group_id 和 external_system_id
        Set<Long> repoGroupIds = projects.stream()
            .map(RepositoryProjectDTO::getRepoGroupId)
            .filter(id -> id != null)
            .collect(Collectors.toSet());
        
        Set<Long> externalSystemIds = projects.stream()
            .map(RepositoryProjectDTO::getExternalSystemId)
            .filter(id -> id != null)
            .collect(Collectors.toSet());

        // 2. 批量查询仓库组信息（需要同时匹配 repo_group_id 和 external_system_id）
        Map<String, RepositoryGroup> repoGroupMap = Collections.emptyMap();
        if (!repoGroupIds.isEmpty()) {
            List<Long> repoGroupIdList = new ArrayList<>(repoGroupIds);
            // 使用复合键：externalSystemId + "_" + repoGroupId 避免不同系统的 ID 冲突
            repoGroupMap = repositoryGroupRepository.findByRepoGroupIdIn(repoGroupIdList).stream()
                .filter(group -> externalSystemIds.contains(group.getExternalSystemId()))
                .collect(Collectors.toMap(
                    group -> group.getExternalSystemId() + "_" + group.getRepoGroupId(),
                    Function.identity()
                ));
        }

        // 3. 填充仓库组名称（使用复合键：externalSystemId + "_" + repoGroupId）
        Map<String, RepositoryGroup> finalRepoGroupMap = repoGroupMap;
        projects.forEach(project -> {
            if (project.getRepoGroupId() != null && project.getExternalSystemId() != null) {
                String key = project.getExternalSystemId() + "_" + project.getRepoGroupId();
                RepositoryGroup group = finalRepoGroupMap.get(key);
                if (group != null) {
                    project.setRepoGroupName(group.getName());
                }
            }
        });

        // 4. 填充分支数量
        fillBranchCounts(projects);
    }

    /**
     * 批量填充项目的分支数量（解决N+1查询问题）
     */
    private void fillBranchCounts(List<RepositoryProjectDTO> projects) {
        if (projects.isEmpty()) {
            return;
        }
        
        // 1. 收集所有项目ID
        Set<Long> projectIds = projects.stream()
            .map(RepositoryProjectDTO::getId)
            .collect(Collectors.toSet());
        
        // 2. 批量查询分支数量（1条SQL）
        List<Object[]> countResults = repositoryBranchRepository.countByProjectIds(projectIds);
        Map<Long, Long> branchCountMap = countResults.stream()
            .collect(Collectors.toMap(
                arr -> (Long) arr[0],  // projectId
                arr -> (Long) arr[1]   // count
            ));
        
        // 3. 填充数据
        projects.forEach(project -> {
            Long branchCount = branchCountMap.getOrDefault(project.getId(), 0L);
            project.setBranchCount(branchCount);
        });
    }
}