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

import com.qqchen.deploy.backend.deploy.converter.TeamApplicationConverter;
import com.qqchen.deploy.backend.deploy.dto.TeamApplicationDTO;
import com.qqchen.deploy.backend.deploy.entity.Application;
import com.qqchen.deploy.backend.deploy.entity.DeployRecord;
import com.qqchen.deploy.backend.deploy.entity.Environment;
import com.qqchen.deploy.backend.deploy.entity.Team;
import com.qqchen.deploy.backend.deploy.entity.TeamApplication;
import com.qqchen.deploy.backend.deploy.enums.BuildTypeEnum;
import com.qqchen.deploy.backend.deploy.query.TeamApplicationQuery;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.RepositoryProject;
import com.qqchen.deploy.backend.deploy.repository.IApplicationRepository;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositoryProjectRepository;
import com.qqchen.deploy.backend.deploy.repository.IDeployRecordRepository;
import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository;
import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository;
import com.qqchen.deploy.backend.deploy.repository.ITeamRepository;
import com.qqchen.deploy.backend.deploy.service.ITeamApplicationService;
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.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TeamApplicationServiceImpl extends BaseServiceImpl<TeamApplication, TeamApplicationDTO, TeamApplicationQuery, Long> implements ITeamApplicationService {

    @Resource
    private ITeamApplicationRepository teamApplicationRepository;

    @Resource
    private ITeamRepository teamRepository;

    @Resource
    private IApplicationRepository applicationRepository;

    @Resource
    private IDeployRecordRepository deployRecordRepository;

    @Resource
    private IEnvironmentRepository environmentRepository;

    @Resource
    private IWorkflowDefinitionRepository workflowDefinitionRepository;

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private IRepositoryProjectRepository repositoryProjectRepository;

    @Resource
    private TeamApplicationConverter teamApplicationConverter;

    @Override
    @Transactional
    public TeamApplicationDTO create(TeamApplicationDTO dto) {
        return super.create(dto);
    }

    /**
     * 重写父类的唯一性约束校验方法
     * <p>校验规则：
     * <ul>
     *   <li>1. 检查团队+应用+环境组合是否已存在</li>
     *   <li>2. 检查团队+环境+Jenkins Job 组合是否已存在（仅当构建类型为 JENKINS 时）</li>
     * </ul>
     */
    @Override
    protected void validateUniqueConstraints(TeamApplicationDTO dto) {
        // 1. 检查团队+应用+环境组合是否已存在
        if (teamApplicationRepository.existsByTeamIdAndApplicationIdAndEnvironmentIdAndDeletedFalse(
                dto.getTeamId(), dto.getApplicationId(), dto.getEnvironmentId())) {
            throw new BusinessException(ResponseCode.TEAM_APPLICATION_ALREADY_EXISTS);
        }

        // 2. 检查团队+环境+Jenkins Job 是否已存在（仅当构建类型为 JENKINS 且 deployJob 不为空时）
        if (dto.getBuildType() == BuildTypeEnum.JENKINS 
                && dto.getDeployJob() != null 
                && !dto.getDeployJob().trim().isEmpty()) {
            if (teamApplicationRepository.existsByTeamIdAndEnvironmentIdAndDeployJobAndDeletedFalse(
                    dto.getTeamId(), dto.getEnvironmentId(), dto.getDeployJob())) {
                throw new BusinessException(ResponseCode.TEAM_APPLICATION_DEPLOY_JOB_EXISTS);
            }
        }
    }

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

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

    /**
     * 批量填充扩展字段：teamName、applicationName、applicationCode、environmentName、workflowDefinitionName、deploySystemName
     *
     * <p>使用批量查询避免N+1问题
     */
    private void fillExtendedFields(List<TeamApplicationDTO> teamApps) {
        if (teamApps.isEmpty()) {
            return;
        }

        // 1. 收集所有需要查询的ID
        Set<Long> teamIds = teamApps.stream()
            .map(TeamApplicationDTO::getTeamId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Set<Long> applicationIds = teamApps.stream()
            .map(TeamApplicationDTO::getApplicationId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Set<Long> environmentIds = teamApps.stream()
            .map(TeamApplicationDTO::getEnvironmentId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Set<Long> workflowDefinitionIds = teamApps.stream()
            .map(TeamApplicationDTO::getWorkflowDefinitionId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Set<Long> deploySystemIds = teamApps.stream()
            .map(TeamApplicationDTO::getDeploySystemId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Set<Long> codeSourceSystemIds = teamApps.stream()
            .map(TeamApplicationDTO::getSourceGitSystemId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        // 收集源Git项目查询条件（systemId + repoProjectId）
        record SourceProjectKey(Long systemId, Long repoProjectId) {}
        Set<SourceProjectKey> sourceProjectKeys = teamApps.stream()
            .filter(app -> app.getSourceGitSystemId() != null && app.getSourceGitProjectId() != null)
            .map(app -> new SourceProjectKey(app.getSourceGitSystemId(), app.getSourceGitProjectId()))
            .collect(Collectors.toSet());

        // 收集目标Git项目查询条件（systemId + repoProjectId）
        record TargetProjectKey(Long systemId, Long repoProjectId) {}
        Set<TargetProjectKey> targetProjectKeys = teamApps.stream()
            .filter(app -> app.getTargetGitSystemId() != null && app.getTargetGitProjectId() != null)
            .map(app -> new TargetProjectKey(app.getTargetGitSystemId(), app.getTargetGitProjectId()))
            .collect(Collectors.toSet());
        
        Set<Long> targetGitSystemIds = teamApps.stream()
            .map(TeamApplicationDTO::getTargetGitSystemId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        // 2. 批量查询团队信息
        Map<Long, Team> teamMap = new HashMap<>();
        if (!teamIds.isEmpty()) {
            teamRepository.findAllById(teamIds).forEach(team ->
                teamMap.put(team.getId(), team)
            );
        }

        // 3. 批量查询应用信息
        Map<Long, Application> appMap = new HashMap<>();
        if (!applicationIds.isEmpty()) {
            applicationRepository.findAllById(applicationIds).forEach(app ->
                appMap.put(app.getId(), app)
            );
        }

        // 4. 批量查询环境信息
        Map<Long, Environment> envMap = new HashMap<>();
        if (!environmentIds.isEmpty()) {
            environmentRepository.findAllById(environmentIds).forEach(env ->
                envMap.put(env.getId(), env)
            );
        }

        // 5. 批量查询工作流定义信息
        Map<Long, WorkflowDefinition> workflowMap = new HashMap<>();
        if (!workflowDefinitionIds.isEmpty()) {
            workflowDefinitionRepository.findAllById(workflowDefinitionIds).forEach(workflow ->
                workflowMap.put(workflow.getId(), workflow)
            );
        }

        // 6. 批量查询部署系统信息（Jenkins）
        Map<Long, ExternalSystem> deploySystemMap = new HashMap<>();
        if (!deploySystemIds.isEmpty()) {
            externalSystemRepository.findAllById(deploySystemIds).forEach(system ->
                deploySystemMap.put(system.getId(), system)
            );
        }

        // 7. 批量查询代码源系统信息（Git）
        Map<Long, ExternalSystem> codeSourceSystemMap = new HashMap<>();
        if (!codeSourceSystemIds.isEmpty()) {
            externalSystemRepository.findAllById(codeSourceSystemIds).forEach(system ->
                codeSourceSystemMap.put(system.getId(), system)
            );
        }

        // 8. 批量查询源Git项目信息（按 systemId + repoProjectId）
        Map<String, RepositoryProject> codeSourceProjectMap = new HashMap<>();
        for (SourceProjectKey key : sourceProjectKeys) {
            repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse(
                key.repoProjectId(), key.systemId()
            ).ifPresent(project -> 
                codeSourceProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project)
            );
        }

        // 9. 批量查询目标Git系统信息
        Map<Long, ExternalSystem> targetGitSystemMap = new HashMap<>();
        if (!targetGitSystemIds.isEmpty()) {
            externalSystemRepository.findAllById(targetGitSystemIds).forEach(system ->
                targetGitSystemMap.put(system.getId(), system)
            );
        }

        // 10. 批量查询目标Git项目信息（按 systemId + repoProjectId）
        Map<String, RepositoryProject> targetGitProjectMap = new HashMap<>();
        for (TargetProjectKey key : targetProjectKeys) {
            repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse(
                key.repoProjectId(), key.systemId()
            ).ifPresent(project -> 
                targetGitProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project)
            );
        }

        // 11. 填充扩展字段
        teamApps.forEach(teamApp -> {
            // 填充团队名称
            if (teamApp.getTeamId() != null) {
                Team team = teamMap.get(teamApp.getTeamId());
                if (team != null) {
                    teamApp.setTeamName(team.getTeamName());
                }
            }

            // 填充应用信息
            if (teamApp.getApplicationId() != null) {
                Application app = appMap.get(teamApp.getApplicationId());
                if (app != null) {
                    teamApp.setApplicationName(app.getAppName());
                    teamApp.setApplicationCode(app.getAppCode());
                }
            }

            // 填充环境名称
            if (teamApp.getEnvironmentId() != null) {
                Environment env = envMap.get(teamApp.getEnvironmentId());
                if (env != null) {
                    teamApp.setEnvironmentName(env.getEnvName());
                }
            }

            // 填充工作流定义名称
            if (teamApp.getWorkflowDefinitionId() != null) {
                WorkflowDefinition workflow = workflowMap.get(teamApp.getWorkflowDefinitionId());
                if (workflow != null) {
                    teamApp.setWorkflowDefinitionName(workflow.getName());
                }
            }

            // 填充部署系统名称
            if (teamApp.getDeploySystemId() != null) {
                ExternalSystem system = deploySystemMap.get(teamApp.getDeploySystemId());
                if (system != null) {
                    teamApp.setDeploySystemName(system.getName());
                }
            }

            // 填充源Git系统名称
            if (teamApp.getSourceGitSystemId() != null) {
                ExternalSystem system = codeSourceSystemMap.get(teamApp.getSourceGitSystemId());
                if (system != null) {
                    teamApp.setSourceGitSystemName(system.getName());
                }
            }

            // 填充源Git项目名称
            if (teamApp.getSourceGitSystemId() != null && teamApp.getSourceGitProjectId() != null) {
                String key = teamApp.getSourceGitSystemId() + "_" + teamApp.getSourceGitProjectId();
                RepositoryProject project = codeSourceProjectMap.get(key);
                if (project != null) {
                    teamApp.setSourceGitProjectName(project.getName());
                }
            }

            // 填充目标Git系统名称
            if (teamApp.getTargetGitSystemId() != null) {
                ExternalSystem system = targetGitSystemMap.get(teamApp.getTargetGitSystemId());
                if (system != null) {
                    teamApp.setTargetGitSystemName(system.getName());
                }
            }

            // 填充目标Git项目名称
            if (teamApp.getTargetGitSystemId() != null && teamApp.getTargetGitProjectId() != null) {
                String key = teamApp.getTargetGitSystemId() + "_" + teamApp.getTargetGitProjectId();
                RepositoryProject project = targetGitProjectMap.get(key);
                if (project != null) {
                    teamApp.setTargetGitProjectName(project.getName());
                }
            }
        });
    }

    /**
     * 删除团队应用及其关联的部署记录
     *
     * <p>删除逻辑：
     * <ul>
     *   <li>1. 先物理删除所有关联的部署记录（DeployRecord）</li>
     *   <li>2. 再物理删除团队应用（TeamApplication）</li>
     * </ul>
     *
     * <p>注意：
     * <ul>
     *   <li>TeamApplication 使用物理删除（@LogicDelete(false)）</li>
     *   <li>DeployRecord 由于外键约束，必须物理删除</li>
     * </ul>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        log.info("开始删除团队应用，ID: {}", id);

        // 1. 查询关联的部署记录
        List<DeployRecord> deployRecords = deployRecordRepository.findByTeamApplicationId(id);

        if (!deployRecords.isEmpty()) {
            log.info("团队应用 {} 关联了 {} 条部署记录，开始级联删除", id, deployRecords.size());

            // 2. 物理删除所有关联的部署记录（避免外键约束冲突）
            deployRecordRepository.deleteAll(deployRecords);

            log.info("成功物理删除 {} 条部署记录", deployRecords.size());
        } else {
            log.debug("团队应用 {} 没有关联的部署记录", id);
        }

        // 3. 物理删除团队应用
        super.delete(id);

        log.info("成功删除团队应用，ID: {}", id);
    }
}

