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

import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentConfigDTO;
import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentNotificationConfigDTO;
import com.qqchen.deploy.backend.deploy.entity.Environment;
import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig;
import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentNotificationConfig;
import com.qqchen.deploy.backend.deploy.query.TeamEnvironmentConfigQuery;
import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository;
import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository;
import com.qqchen.deploy.backend.deploy.repository.ITeamEnvironmentConfigRepository;
import com.qqchen.deploy.backend.deploy.repository.ITeamEnvironmentNotificationConfigRepository;
import com.qqchen.deploy.backend.deploy.converter.TeamEnvironmentNotificationConfigConverter;
import com.qqchen.deploy.backend.deploy.service.ITeamEnvironmentConfigService;
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.notification.entity.NotificationChannel;
import com.qqchen.deploy.backend.notification.entity.NotificationTemplate;
import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository;
import com.qqchen.deploy.backend.notification.repository.INotificationTemplateRepository;
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;

/**
 * 团队环境配置Service实现
 *
 * @author qqchen
 * @since 2025-11-04
 */
@Slf4j
@Service
public class TeamEnvironmentConfigServiceImpl
    extends BaseServiceImpl<TeamEnvironmentConfig, TeamEnvironmentConfigDTO, TeamEnvironmentConfigQuery, Long>
    implements ITeamEnvironmentConfigService {

    @Resource
    private ITeamEnvironmentConfigRepository teamEnvironmentConfigRepository;

    @Resource
    private IEnvironmentRepository environmentRepository;

    @Resource
    private INotificationChannelRepository notificationChannelRepository;

    @Resource
    private INotificationTemplateRepository notificationTemplateRepository;

    @Resource
    private ITeamApplicationRepository teamApplicationRepository;

    @Resource
    private ITeamEnvironmentNotificationConfigRepository teamEnvironmentNotificationConfigRepository;

    @Resource
    private TeamEnvironmentNotificationConfigConverter notificationConfigConverter;

    @Override
    public List<TeamEnvironmentConfigDTO> findByTeamId(Long teamId) {
        List<TeamEnvironmentConfigDTO> list = teamEnvironmentConfigRepository.findByTeamIdOrderByIdAsc(teamId).stream()
            .map(converter::toDto)
            .collect(Collectors.toList());
        fillExtendedFields(list);
        return list;
    }

    @Override
    public TeamEnvironmentConfigDTO findByTeamIdAndEnvironmentId(Long teamId, Long environmentId) {
        TeamEnvironmentConfigDTO dto = teamEnvironmentConfigRepository.findByTeamIdAndEnvironmentId(teamId, environmentId)
            .map(converter::toDto)
            .orElse(null);
        if (dto != null) {
            fillExtendedFields(Collections.singletonList(dto));
        }
        return dto;
    }

    @Override
    public boolean exists(Long teamId, Long environmentId) {
        return teamEnvironmentConfigRepository.existsByTeamIdAndEnvironmentId(teamId, environmentId);
    }

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

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

    /**
     * 重写创建方法，添加数据一致性兜底逻辑
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TeamEnvironmentConfigDTO create(TeamEnvironmentConfigDTO dto) {
        // 0) 前置处理：数据归一化和业务规则
        prepareForCreate(dto);

        // 1) 创建主配置（会自动调用 validateUniqueConstraints）
        TeamEnvironmentConfigDTO result = super.create(dto);

        // 2) 保存通知配置（基于 teamId + environmentId）
        saveOrUpdateNotificationConfig(dto);

        // 3) 附带扩展信息（环境名、通知配置含渠道名、应用数量）
        attachSnapshot(result);
        return result;
    }

    /**
     * 重写更新方法，添加数据一致性兜底逻辑
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TeamEnvironmentConfigDTO update(Long id, TeamEnvironmentConfigDTO dto) {
        // 0) 前置处理：数据归一化、业务规则和维度验证
        prepareForUpdate(id, dto);

        // 1) 更新主配置（会自动调用 validateUniqueConstraints）
        TeamEnvironmentConfigDTO result = super.update(id, dto);

        // 2) 保存通知配置
        saveOrUpdateNotificationConfig(dto);

        // 3) 附带扩展信息（环境名、通知配置含渠道名、应用数量）
        attachSnapshot(result);
        return result;
    }

    /**
     * 校验唯一性约束
     *
     * @param dto 团队环境配置DTO
     */
    @Override
    protected void validateUniqueConstraints(TeamEnvironmentConfigDTO dto) {
        if (dto == null || dto.getTeamId() == null || dto.getEnvironmentId() == null) {
            return;
        }

        // 检查 teamId + environmentId 组合唯一性
        if (teamEnvironmentConfigRepository.existsByTeamIdAndEnvironmentId(dto.getTeamId(), dto.getEnvironmentId())) {
            throw new BusinessException(ResponseCode.CONFLICT, new Object[]{"团队/环境配置已存在"});
        }
    }

    /**
     * 数据一致性兜底逻辑
     * <p>规则：
     * <ul>
     *   <li>1. 如果不需要审批（approvalRequired = false），清空审批人列表</li>
     * </ul>
     *
     * <p>注意：通知相关配置已迁移到 deploy_team_environment_notification_config 表，不在此处理
     */
    private void applyDataConsistencyRules(TeamEnvironmentConfigDTO dto) {
        if (dto == null) {
            return;
        }

        // 规则1：不需要审批时，清空审批人
        if (dto.getApprovalRequired() != null && !dto.getApprovalRequired()) {
            if (dto.getApproverUserIds() != null && !dto.getApproverUserIds().isEmpty()) {
                log.info("兜底逻辑触发：团队 {} 环境 {} 不需要审批，清空审批人列表 {}",
                    dto.getTeamId(), dto.getEnvironmentId(), dto.getApproverUserIds());
                dto.setApproverUserIds(null);
            }
        }
    }

    /**
     * 保存或更新通知配置
     * <p>如果 DTO 中包含 notificationConfig，则：
     * <ul>
     *   <li>如果数据库中已存在对应记录，则更新</li>
     *   <li>如果不存在，则创建新记录</li>
     * </ul>
     *
     * @param dto 团队环境配置DTO（包含 notificationConfig）
     */
    private void saveOrUpdateNotificationConfig(TeamEnvironmentConfigDTO dto) {
        if (dto == null || dto.getNotificationConfig() == null) {
            return;
        }

        TeamEnvironmentNotificationConfigDTO notificationDTO = dto.getNotificationConfig();

        // 确保 teamId 和 environmentId 一致
        notificationDTO.setTeamId(dto.getTeamId());
        notificationDTO.setEnvironmentId(dto.getEnvironmentId());

        // 查询是否已存在记录
        Optional<TeamEnvironmentNotificationConfig> existingConfig =
            teamEnvironmentNotificationConfigRepository.findByTeamIdAndEnvironmentId(
                dto.getTeamId(), dto.getEnvironmentId()
            );

        if (existingConfig.isPresent()) {
            // 更新现有记录
            TeamEnvironmentNotificationConfig config = existingConfig.get();
            notificationConfigConverter.updateEntity(config, notificationDTO);
            teamEnvironmentNotificationConfigRepository.save(config);
            log.info("更新通知配置: teamId={}, environmentId={}", dto.getTeamId(), dto.getEnvironmentId());
        } else {
            // 创建新记录
            TeamEnvironmentNotificationConfig newConfig = notificationConfigConverter.toEntity(notificationDTO);
            teamEnvironmentNotificationConfigRepository.save(newConfig);
            log.info("创建通知配置: teamId={}, environmentId={}", dto.getTeamId(), dto.getEnvironmentId());
        }
    }

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

        // 1. 收集所有团队ID和环境ID
        Set<Long> teamIds = configs.stream()
            .map(TeamEnvironmentConfigDTO::getTeamId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

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

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

        // 3. 批量查询通知配置（来自 deploy_team_environment_notification_config 表）
        Map<String, TeamEnvironmentNotificationConfig> notificationConfigMap = new HashMap<>();
        if (!teamIds.isEmpty() && !environmentIds.isEmpty()) {
            List<TeamEnvironmentNotificationConfig> notificationConfigs =
                teamEnvironmentNotificationConfigRepository.findByTeamIdInAndEnvironmentIdIn(teamIds, environmentIds);

            notificationConfigs.forEach(nc -> {
                String key = nc.getTeamId() + "_" + nc.getEnvironmentId();
                notificationConfigMap.put(key, nc);
            });
        }

        // 4. 批量查询通知渠道信息
        Set<Long> channelIds = notificationConfigMap.values().stream()
            .map(TeamEnvironmentNotificationConfig::getNotificationChannelId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Map<Long, NotificationChannel> channelMap = new HashMap<>();
        if (!channelIds.isEmpty()) {
            notificationChannelRepository.findAllById(channelIds).forEach(channel ->
                channelMap.put(channel.getId(), channel)
            );
        }

        // 5. 批量查询通知模板信息
        Set<Long> templateIds = new HashSet<>();
        notificationConfigMap.values().forEach(nc -> {
            if (nc.getPreApprovalNotificationTemplateId() != null) {
                templateIds.add(nc.getPreApprovalNotificationTemplateId());
            }
            if (nc.getBuildNotificationTemplateId() != null) {
                templateIds.add(nc.getBuildNotificationTemplateId());
            }
        });

        Map<Long, NotificationTemplate> templateMap = new HashMap<>();
        if (!templateIds.isEmpty()) {
            notificationTemplateRepository.findAllById(templateIds).forEach(template ->
                templateMap.put(template.getId(), template)
            );
        }

        // 6. 填充扩展字段
        configs.forEach(config -> {
            // 填充环境名称
            if (config.getEnvironmentId() != null) {
                Environment env = environmentMap.get(config.getEnvironmentId());
                if (env != null) {
                    config.setEnvironmentName(env.getEnvName());
                }
            }

            // 填充通知配置
            if (config.getTeamId() != null && config.getEnvironmentId() != null) {
                String key = config.getTeamId() + "_" + config.getEnvironmentId();
                TeamEnvironmentNotificationConfig notificationConfig = notificationConfigMap.get(key);

                if (notificationConfig != null) {
                    TeamEnvironmentNotificationConfigDTO notificationConfigDTO =
                        notificationConfigConverter.toDto(notificationConfig);

                    // 填充通知渠道名称
                    if (notificationConfig.getNotificationChannelId() != null) {
                        NotificationChannel channel = channelMap.get(notificationConfig.getNotificationChannelId());
                        if (channel != null) {
                            notificationConfigDTO.setNotificationChannelName(channel.getName());
                        }
                    }

                    // 填充审批前提醒模板名称
                    if (notificationConfig.getPreApprovalNotificationTemplateId() != null) {
                        NotificationTemplate template = templateMap.get(notificationConfig.getPreApprovalNotificationTemplateId());
                        if (template != null) {
                            notificationConfigDTO.setPreApprovalNotificationTemplateName(template.getName());
                        }
                    }

                    // 填充构建通知模板名称
                    if (notificationConfig.getBuildNotificationTemplateId() != null) {
                        NotificationTemplate template = templateMap.get(notificationConfig.getBuildNotificationTemplateId());
                        if (template != null) {
                            notificationConfigDTO.setBuildNotificationTemplateName(template.getName());
                        }
                    }

                    config.setNotificationConfig(notificationConfigDTO);
                }
            }

            // 填充应用数量统计
            if (config.getTeamId() != null && config.getEnvironmentId() != null) {
                Long appCount = teamApplicationRepository.countByTeamIdAndEnvironmentId(
                    config.getTeamId(),
                    config.getEnvironmentId()
                );
                config.setApplicationCount(appCount != null ? appCount : 0L);
            }
        });
    }

    /**
     * 重写删除方法，删除前校验该团队环境下是否有绑定的应用，并级联删除通知配置
     *
     * @param id 团队环境配置ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        // 1. 查询团队环境配置
        TeamEnvironmentConfig config = teamEnvironmentConfigRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ResponseCode.TEAM_CONFIG_NOT_FOUND, new Object[] {id}));

        // 2. 校验该环境下是否有绑定的应用
        Long appCount = teamApplicationRepository.countByTeamIdAndEnvironmentId(
            config.getTeamId(),
            config.getEnvironmentId()
        );

        if (appCount != null && appCount > 0) {
            log.warn("删除团队环境配置失败: 团队 {} 的环境 {} 下存在 {} 个绑定应用",
                config.getTeamId(), config.getEnvironmentId(), appCount);
            throw new BusinessException(ResponseCode.TEAM_ENVIRONMENT_HAS_APPLICATIONS, new Object[] {appCount});
        }

        //3. 删除关联的通知配置
        Optional<TeamEnvironmentNotificationConfig> notificationConfig =
            teamEnvironmentNotificationConfigRepository.findByTeamIdAndEnvironmentId(
                config.getTeamId(),
                config.getEnvironmentId()
            );

        if (notificationConfig.isPresent()) {
            teamEnvironmentNotificationConfigRepository.deleteById(notificationConfig.get().getId());
            log.info("删除关联的通知配置: teamId={}, environmentId={}",
                config.getTeamId(), config.getEnvironmentId());
        }

        // 4. 执行删除（逻辑删除）
        super.delete(id);
        log.info("成功删除团队环境配置: id={}, teamId={}, environmentId={}",
            id, config.getTeamId(), config.getEnvironmentId());
    }

/**
     * 创建前预处理
     * 包含数据归一化和业务规则应用
     */
    private void prepareForCreate(TeamEnvironmentConfigDTO dto) {
        if (dto == null) {
            return;
        }

        // 1) 应用业务规则
        applyDataConsistencyRules(dto);

        // 2) 数据归一化
        normalizeDataFields(dto);
    }

    /**
     * 更新前预处理
     * 包含数据归一化、业务规则应用和维度验证
     */
    private void prepareForUpdate(Long id, TeamEnvironmentConfigDTO dto) {
        if (dto == null) {
            return;
        }

        // 1) 验证维度不可变更
        validateDimensionChangeOnUpdate(id, dto);

        // 2) 应用业务规则
        applyDataConsistencyRules(dto);

        // 3) 数据归一化
        normalizeDataFields(dto);
    }

    /**
     * 数据字段归一化
     * 处理空值、格式标准化等
     */
    private void normalizeDataFields(TeamEnvironmentConfigDTO dto) {
        if (dto == null) {
            return;
        }

        // 空集合归一化
        if (dto.getApproverUserIds() != null && dto.getApproverUserIds().isEmpty()) {
            dto.setApproverUserIds(null);
        }

        // 归一化通知配置的 team/environment（以主 DTO 为准）
        if (dto.getNotificationConfig() != null) {
            TeamEnvironmentNotificationConfigDTO nc = dto.getNotificationConfig();
            nc.setTeamId(dto.getTeamId());
            nc.setEnvironmentId(dto.getEnvironmentId());
        }
    }

    /**
     * 验证更新时维度不可变更
     */
    private void validateDimensionChangeOnUpdate(Long id, TeamEnvironmentConfigDTO dto) {
        TeamEnvironmentConfig origin = teamEnvironmentConfigRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ResponseCode.TEAM_CONFIG_NOT_FOUND, new Object[] {id}));

        // 禁止修改 teamId 和 environmentId
        if (dto.getTeamId() != null && !Objects.equals(dto.getTeamId(), origin.getTeamId())) {
            throw new BusinessException(ResponseCode.CONFLICT, new Object[]{"不允许变更 teamId"});
        }
        if (dto.getEnvironmentId() != null && !Objects.equals(dto.getEnvironmentId(), origin.getEnvironmentId())) {
            throw new BusinessException(ResponseCode.CONFLICT, new Object[]{"不允许变更 environmentId"});
        }

        // 将主维度补齐，确保后续处理使用
        dto.setTeamId(origin.getTeamId());
        dto.setEnvironmentId(origin.getEnvironmentId());
    }


    /**
     * 在返回结果上附带扩展数据快照（环境名、通知配置含渠道名、应用数量）
     */
    private void attachSnapshot(TeamEnvironmentConfigDTO result) {
        if (result == null) {
            return;
        }
        // 复用已有批量填充逻辑（单元素列表）
        List<TeamEnvironmentConfigDTO> list = new ArrayList<>();
        list.add(result);
        fillExtendedFields(list);
    }
}

