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

import com.qqchen.deploy.backend.deploy.dto.*;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsSyncHistory;
import com.qqchen.deploy.backend.deploy.entity.RepositorySyncHistory;
import com.qqchen.deploy.backend.deploy.enums.ExternalSystemSyncStatus;
import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType;
import com.qqchen.deploy.backend.deploy.enums.RepositorySyncType;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsSyncHistoryRepository;
import com.qqchen.deploy.backend.deploy.repository.IRepositorySyncHistoryRepository;
import com.qqchen.deploy.backend.deploy.service.*;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsJobService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsViewService;
import com.qqchen.deploy.backend.system.enums.ExternalSystemAuthTypeEnum;
import com.qqchen.deploy.backend.system.enums.ExternalSystemTypeEnum;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.exception.UniqueConstraintException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.framework.utils.SensitiveDataEncryptor;
import com.qqchen.deploy.backend.deploy.integration.IExternalSystemIntegration;
import com.qqchen.deploy.backend.system.model.ExternalSystemDTO;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.service.IExternalSystemService;
import com.qqchen.deploy.backend.system.model.query.ExternalSystemQuery;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.qqchen.deploy.backend.framework.annotation.ServiceType.Type.DATABASE;

/**
 * 外部系统服务实现类
 * 主要功能：
 * 1. 外部系统的CRUD操作
 * 2. 系统连接测试
 * 3. 系统数据同步
 * 4. 系统状态管理
 *
 * @author QQChen
 * @version 1.0.0
 */
@Slf4j
@Service
@ServiceType(DATABASE)
public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, ExternalSystemDTO, ExternalSystemQuery, Long> implements IExternalSystemService {

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private List<IExternalSystemIntegration> systemIntegrations;
    
    private Map<ExternalSystemTypeEnum, IExternalSystemIntegration> integrationMap;

    @Resource
    private IJenkinsViewService jenkinsViewService;

    @Resource
    private IJenkinsJobService jenkinsJobService;

    @Resource
    private IJenkinsBuildService jenkinsBuildService;

    @Resource
    private IJenkinsSyncHistoryRepository jenkinsSyncHistoryRepository;

    @Resource
    private IRepositoryGroupService repositoryGroupService;

    @Resource
    private IRepositoryProjectService repositoryProjectService;

    @Resource
    private IRepositoryBranchService repositoryBranchService;

    @Resource
    private IRepositorySyncHistoryRepository repositorySyncHistoryRepository;
    
    @Resource
    private SensitiveDataEncryptor encryptor;
    
    @PostConstruct
    public void init() {
        integrationMap = systemIntegrations.stream()
            .collect(Collectors.toMap(
                IExternalSystemIntegration::getSystemType,
                integration -> integration
            ));
    }

    @Override
    public void validateUniqueConstraints(ExternalSystemDTO dto) {
        // 检查名称唯一性
        if (externalSystemRepository.existsByNameAndDeletedFalse(dto.getName())) {
            throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_NAME_EXISTS, "name", dto.getName());
        }
        
        // 检查类型+URL组合唯一性
        if (externalSystemRepository.existsByTypeAndUrlAndDeletedFalse(dto.getType(), dto.getUrl())) {
            throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_TYPE_URL_EXISTS, 
                "type and url", dto.getType() + ":" + dto.getUrl());
        }

        validateGitAuth(dto);
    }

    @Override
    public ExternalSystemDTO create(ExternalSystemDTO dto) {
        // 加密密码和Token
        encryptSensitiveData(dto);
        return super.create(dto);
    }

    @Override
    public ExternalSystemDTO update(Long id, ExternalSystemDTO dto) {
        // 先验证Git认证
        validateGitAuth(dto);
        
        // 获取现有系统
        ExternalSystem existingSystem = findEntityById(id);
        
        // 密码处理
        if (SensitiveDataEncryptor.isMasked(dto.getPassword())) {
            // 如果是掩码，保留原密码
            dto.setPassword(existingSystem.getPassword());
            log.debug("保留原密码（掩码）：系统ID={}", id);
        } else if (StringUtils.isNotBlank(dto.getPassword())) {
            // 如果是新密码，加密后保存
            dto.setPassword(encryptor.encrypt(dto.getPassword()));
            log.debug("更新并加密新密码：系统ID={}", id);
        } else {
            // 如果为空，保留原密码
            dto.setPassword(existingSystem.getPassword());
            log.debug("保留原密码（空值）：系统ID={}", id);
        }
        
        // Token处理
        if (SensitiveDataEncryptor.isMasked(dto.getToken())) {
            // 如果是掩码，保留原Token
            dto.setToken(existingSystem.getToken());
            log.debug("保留原Token（掩码）：系统ID={}", id);
        } else if (StringUtils.isNotBlank(dto.getToken())) {
            // 如果是新Token，加密后保存
            dto.setToken(encryptor.encrypt(dto.getToken()));
            log.debug("更新并加密新Token：系统ID={}", id);
        } else {
            // 如果为空，保留原Token
            dto.setToken(existingSystem.getToken());
            log.debug("保留原Token（空值）：系统ID={}", id);
        }
        
        return super.update(id, dto);
    }

    /**
     * 验证Git系统的认证方式
     *
     * @param dto 外部系统DTO
     */
    private void validateGitAuth(ExternalSystemDTO dto) {
        if (dto.getType() == ExternalSystemTypeEnum.GIT) {
            if (dto.getAuthType() != ExternalSystemAuthTypeEnum.TOKEN) {
                throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR);
            }
            if (StringUtils.isBlank(dto.getToken())) {
                throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_TOKEN_REQUIRED);
            }
        }
    }

    @Override
    public ExternalSystemDTO findById(Long id) {
        ExternalSystemDTO dto = super.findById(id);
        // 查询后处理：用掩码替换敏感数据
        maskSensitiveData(dto);
        return dto;
    }

    @Override
    public List<ExternalSystemDTO> findAll() {
        List<ExternalSystemDTO> dtos = super.findAll();
        // 查询后处理：用掩码替换敏感数据
        dtos.forEach(this::maskSensitiveData);
        return dtos;
    }

    @Override
    public org.springframework.data.domain.Page<ExternalSystemDTO> page(ExternalSystemQuery query) {
        org.springframework.data.domain.Page<ExternalSystemDTO> page = super.page(query);
        // 查询后处理：用掩码替换敏感数据
        page.getContent().forEach(this::maskSensitiveData);
        return page;
    }

    @Override
    public List<ExternalSystemDTO> findAll(ExternalSystemQuery query) {
        List<ExternalSystemDTO> dtos = super.findAll(query);
        // 查询后处理：用掩码替换敏感数据
        dtos.forEach(this::maskSensitiveData);
        return dtos;
    }

    @Override
    @Transactional
    public boolean testConnection(Long id) {
        ExternalSystem system = findEntityById(id);
        if (!system.getEnabled()) {
            throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
        }
        
        IExternalSystemIntegration integration = integrationMap.get(system.getType());
        if (integration == null) {
            throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED);
        }
        
        // 直接传入原始系统对象，Integration层会自动处理解密和缓存
        boolean success = integration.testConnection(system);
        if (success) {
            system.setLastConnectTime(LocalDateTime.now());
            externalSystemRepository.save(system);
        }
        return success;
    }

    @Override
    @Transactional
    public void updateStatus(Long id, boolean enabled) {
        ExternalSystem system = findEntityById(id);
        system.setEnabled(enabled);
        externalSystemRepository.save(system);
    }

    @Override
    public JenkinsInstanceDTO getJenkinsInstance(Long externalSystemId) {
        // 1. 创建返回对象
        JenkinsInstanceDTO instanceDTO = new JenkinsInstanceDTO();

        // 2. 获取视图列表
        List<JenkinsViewDTO> views = jenkinsViewService.findByExternalSystemId(externalSystemId);
        instanceDTO.setJenkinsViewList(views);
        instanceDTO.setTotalViews(views.size());

        // 3. 获取任务列表
        List<JenkinsJobDTO> jobs = jenkinsJobService.findByExternalSystemId(externalSystemId);
        instanceDTO.setJenkinsJobList(jobs);
        instanceDTO.setTotalJobs(jobs.size());

        // 4. 获取构建总数
        Long totalBuilds = jenkinsBuildService.countByExternalSystemId(externalSystemId);
        instanceDTO.setTotalBuilds(totalBuilds.intValue());

        // 5. 获取最后同步时间
        JenkinsSyncHistory lastViewSync = jenkinsSyncHistoryRepository.findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
            externalSystemId, JenkinsSyncType.VIEW, ExternalSystemSyncStatus.SUCCESS);
        if (lastViewSync != null) {
            instanceDTO.setLastSyncViewsTime(lastViewSync.getEndTime());
        }

        JenkinsSyncHistory lastJobSync = jenkinsSyncHistoryRepository.findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
            externalSystemId, JenkinsSyncType.JOB, ExternalSystemSyncStatus.SUCCESS);
        if (lastJobSync != null) {
            instanceDTO.setLastSyncJobsTime(lastJobSync.getEndTime());
        }

        JenkinsSyncHistory lastBuildSync = jenkinsSyncHistoryRepository.findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
            externalSystemId, JenkinsSyncType.BUILD, ExternalSystemSyncStatus.SUCCESS);
        if (lastBuildSync != null) {
            instanceDTO.setLastSyncBuildsTime(lastBuildSync.getEndTime());
        }

        return instanceDTO;
    }

    @Override
    public GitInstanceDTO getGitInstance(Long externalSystemId) {
        // 1. 创建返回对象
        GitInstanceDTO instanceDTO = new GitInstanceDTO();

        // 2. 获取仓库组列表
        List<RepositoryGroupDTO> groups = repositoryGroupService.findByExternalSystemId(externalSystemId);
        instanceDTO.setRepositoryGroupList(groups);
        instanceDTO.setTotalGroups(groups.size());

        // 3. 获取项目列表
        List<RepositoryProjectDTO> projects = repositoryProjectService.findByExternalSystemId(externalSystemId);
        instanceDTO.setRepositoryProjectList(projects);
        instanceDTO.setTotalProjects(projects.size());

        // 4. 获取分支总数
        Long totalBranches = repositoryBranchService.countByExternalSystemId(externalSystemId);
        instanceDTO.setTotalBranches(totalBranches.intValue());

        // 5. 获取最后同步时间
        RepositorySyncHistory lastGroupSync = repositorySyncHistoryRepository
            .findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
                externalSystemId, RepositorySyncType.GROUP, ExternalSystemSyncStatus.SUCCESS);
        if (lastGroupSync != null) {
            instanceDTO.setLastSyncGroupsTime(lastGroupSync.getEndTime());
        }

        RepositorySyncHistory lastProjectSync = repositorySyncHistoryRepository
            .findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
                externalSystemId, RepositorySyncType.PROJECT, ExternalSystemSyncStatus.SUCCESS);
        if (lastProjectSync != null) {
            instanceDTO.setLastSyncProjectsTime(lastProjectSync.getEndTime());
        }

        RepositorySyncHistory lastBranchSync = repositorySyncHistoryRepository
            .findTopByExternalSystemIdAndSyncTypeAndStatusOrderByStartTimeDesc(
                externalSystemId, RepositorySyncType.BRANCH, ExternalSystemSyncStatus.SUCCESS);
        if (lastBranchSync != null) {
            instanceDTO.setLastSyncBranchesTime(lastBranchSync.getEndTime());
        }

        return instanceDTO;
    }

    /**
     * 加密敏感数据（密码和Token）
     * 在保存前调用
     */
    private void encryptSensitiveData(ExternalSystemDTO dto) {
        if (StringUtils.isNotBlank(dto.getPassword())) {
            dto.setPassword(encryptor.encrypt(dto.getPassword()));
            log.debug("密码已加密");
        }
        if (StringUtils.isNotBlank(dto.getToken())) {
            dto.setToken(encryptor.encrypt(dto.getToken()));
            log.debug("Token已加密");
        }
    }

    /**
     * 用掩码替换敏感数据
     * 在查询后调用
     */
    private void maskSensitiveData(ExternalSystemDTO dto) {
        if (dto == null) {
            return;
        }
        // 如果有密码，用掩码替换
        dto.setPassword(SensitiveDataEncryptor.maskIfPresent(dto.getPassword()));
        // 如果有Token，用掩码替换
        dto.setToken(SensitiveDataEncryptor.maskIfPresent(dto.getToken()));
    }

    /**
     * 解密系统信息用于实际调用外部系统
     * 创建一个临时对象，不影响原对象
     * 
     * @deprecated 不再使用此方法，解密职责已移至Integration层统一处理
     * 保留此方法仅用于兼容性，实际上不应再调用
     */
    @Deprecated
    private ExternalSystem decryptSystemForUse(ExternalSystem system) {
        log.warn("decryptSystemForUse方法已废弃，请直接使用Integration层的解密机制");
        ExternalSystem decrypted = new ExternalSystem();
        // 复制所有属性
        decrypted.setId(system.getId());
        decrypted.setName(system.getName());
        decrypted.setType(system.getType());
        decrypted.setUrl(system.getUrl());
        decrypted.setAuthType(system.getAuthType());
        decrypted.setUsername(system.getUsername());
        decrypted.setEnabled(system.getEnabled());
        decrypted.setConfig(system.getConfig());
        
        // 解密敏感数据
        if (StringUtils.isNotBlank(system.getPassword())) {
            decrypted.setPassword(encryptor.decrypt(system.getPassword()));
        }
        if (StringUtils.isNotBlank(system.getToken())) {
            decrypted.setToken(encryptor.decrypt(system.getToken()));
        }
        
        return decrypted;
    }

}