package com.infoepoch.pms.agent.platform.model.application;

import com.infoepoch.pms.agent.platform.model.application.dto.CreateModelConfigRequest;
import com.infoepoch.pms.agent.platform.model.application.dto.ModelConfigResponse;
import com.infoepoch.pms.agent.platform.model.application.dto.UpdateModelConfigRequest;
import com.infoepoch.pms.agent.platform.agent.domain.irepository.IAgentRepository;
import com.infoepoch.pms.agent.platform.chatbot.domain.irepository.IChatbotRepository;
import com.infoepoch.pms.agent.platform.model.domain.AiModelConfig;
import com.infoepoch.pms.agent.platform.model.domain.AiModelProvider;
import com.infoepoch.pms.agent.platform.model.domain.irepository.IAiModelConfigRepository;
import com.infoepoch.pms.agent.platform.model.domain.irepository.IAiModelProviderRepository;
import com.infoepoch.pms.agent.platform.shared.exception.BusinessException;
import com.infoepoch.pms.agent.platform.shared.response.ResultCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.List;

/**
 * 模型配置管理服务。
 * <p>
 * 负责某供应商下具体模型配置的 CRUD 操作，
 * 确保同一供应商下不存在相同（模型名称 + 类型）的重复配置。
 * </p>
 */
@Service
@RequiredArgsConstructor
public class ModelConfigService {

    private final IAiModelConfigRepository configRepository;
    private final IAiModelProviderRepository providerRepository;
    private final ModelProvisionService modelProvisionService;
    private final ModelQueryService modelQueryService;
    private final SystemSettingsService systemSettingsService;
    private final IAgentRepository agentRepository;
    private final IChatbotRepository chatbotRepository;

    /**
     * 在指定供应商下创建模型配置。
     *
     * @param providerId 供应商 ID
     * @param req        创建请求 DTO
     * @return 模型配置响应
     * @throws BusinessException PROVIDER_NOT_FOUND 若供应商不存在；RESOURCE_ALREADY_EXISTS 若模型配置重复
     */
    @Transactional
    public ModelConfigResponse create(String providerId, CreateModelConfigRequest req) {
        AiModelProvider provider = getProviderOrThrow(providerId);
        if (configRepository.existsByProviderIdAndModelNameAndModelType(
                providerId, req.getModelName(), req.getModelType())) {
            throw new BusinessException(ResultCode.RESOURCE_ALREADY_EXISTS,
                    "供应商下已存在相同模型名称和类型 [" + req.getModelName() + "/" + req.getModelType() + "]");
        }
        AiModelConfig config = new AiModelConfig(
                provider, req.getModelName(), req.getModelType(),
                req.getDisplayName(), req.getMaxTokens(), req.getTemperature(),
                req.getContextWindow(), req.getVectorDimension(),
                req.getEnableThinking(), req.getMultiModel(), req.getCustomConfig());
        return new ModelConfigResponse(configRepository.save(config));
    }

    /**
     * 查询指定供应商下的所有模型配置，按创建时间倒序排列。
     *
     * @param providerId 供应商 ID
     * @return 模型配置响应列表
     * @throws BusinessException PROVIDER_NOT_FOUND 若供应商不存在
     */
    @Transactional(readOnly = true)
    public List<ModelConfigResponse> listByProvider(String providerId) {
        if (!providerRepository.existsById(providerId)) {
            throw new BusinessException(ResultCode.PROVIDER_NOT_FOUND,
                    "供应商 ID=" + providerId + " 不存在");
        }
        return configRepository.findAllByProviderIdOrderByCreatedAtDesc(providerId)
                .stream()
                .map(modelQueryService::attachProvider)
                .map(ModelConfigResponse::new)
                .toList();
    }

    /**
     * 更新模型配置。
     *
     * @param providerId 供应商 ID（用于校验归属）
     * @param configId   模型配置 ID
     * @param req        更新请求 DTO
     * @return 更新后的模型配置响应
     * @throws BusinessException MODEL_CONFIG_NOT_FOUND 若配置不存在或不属于该供应商；RESOURCE_ALREADY_EXISTS 若重复
     */
    @Transactional
    public ModelConfigResponse update(String providerId, String configId, UpdateModelConfigRequest req) {
        AiModelConfig config = getConfigOrThrow(configId);
        if (!config.getProviderId().equals(providerId)) {
            throw new BusinessException(ResultCode.MODEL_CONFIG_NOT_FOUND,
                    "模型配置 ID=" + configId + " 不属于供应商 ID=" + providerId);
        }
        if (configRepository.existsByProviderIdAndModelNameAndModelTypeAndIdNot(
                providerId, req.getModelName(), req.getModelType(), configId)) {
            throw new BusinessException(ResultCode.RESOURCE_ALREADY_EXISTS,
                    "供应商下已存在相同模型名称和类型 [" + req.getModelName() + "/" + req.getModelType() + "]");
        }
        config.modify(req.getModelName(), req.getModelType(), req.getDisplayName(),
                req.getMaxTokens(), req.getTemperature(), req.getContextWindow(),
                req.getVectorDimension(), req.getEnableThinking(),
                req.getMultiModel(), req.getCustomConfig());
        evictModelCacheAfterCommit(config.getId());
        return new ModelConfigResponse(configRepository.save(config));
    }

    /**
     * 删除模型配置。
     *
     * @param providerId 供应商 ID（用于校验归属）
     * @param configId   模型配置 ID
     * @throws BusinessException MODEL_CONFIG_NOT_FOUND 若配置不存在或不属于该供应商
     */
    @Transactional
    public void delete(String providerId, String configId) {
        AiModelConfig config = getConfigOrThrow(configId);
        if (!config.getProviderId().equals(providerId)) {
            throw new BusinessException(ResultCode.MODEL_CONFIG_NOT_FOUND,
                    "模型配置 ID=" + configId + " 不属于供应商 ID=" + providerId);
        }
        assertConfigNotReferenced(config);
        systemSettingsService.clearDefaultModelReferences(List.of(config.getId()));
        configRepository.delete(config.getId());
        evictModelCacheAfterCommit(config.getId());
    }

    /**
     * 切换模型配置启用/禁用状态。
     *
     * @param providerId 供应商 ID（用于校验归属）
     * @param configId   模型配置 ID
     * @return 更新后的模型配置响应
     * @throws BusinessException MODEL_CONFIG_NOT_FOUND 若配置不存在或不属于该供应商
     */
    @Transactional
    public ModelConfigResponse toggle(String providerId, String configId) {
        AiModelConfig config = getConfigOrThrow(configId);
        if (!config.getProviderId().equals(providerId)) {
            throw new BusinessException(ResultCode.MODEL_CONFIG_NOT_FOUND,
                    "模型配置 ID=" + configId + " 不属于供应商 ID=" + providerId);
        }
        config.toggleActive();
        evictModelCacheAfterCommit(config.getId());
        return new ModelConfigResponse(configRepository.save(config));
    }

    /**
     * 在事务提交后失效指定模型配置缓存。
     *
     * @param configId 模型配置 ID
     */
    private void evictModelCacheAfterCommit(String configId) {
        // 缓存失效必须在事务提交后执行，避免并发请求在旧数据仍未提交时重建并写回旧模型实例。
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            modelProvisionService.evict(configId);
            return;
        }
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            /**
             * 事务提交后失效模型缓存。
             */
            @Override
            public void afterCommit() {
                modelProvisionService.evict(configId);
            }
        });
    }

    /**
     * 按 ID 查询供应商，不存在时抛出业务异常。
     *
     * @param providerId 供应商 ID
     * @return 供应商实体
     */
    private AiModelProvider getProviderOrThrow(String providerId) {
        return providerRepository.findById(providerId)
                .orElseThrow(() -> new BusinessException(ResultCode.PROVIDER_NOT_FOUND,
                        "供应商 ID=" + providerId + " 不存在"));
    }

    /**
     * 按 ID 查询模型配置，不存在时抛出业务异常。
     *
     * @param configId 模型配置 ID
     * @return 模型配置实体
     */
    private AiModelConfig getConfigOrThrow(String configId) {
        return modelQueryService.getModelConfigOrNull(configId);
    }

    private void assertConfigNotReferenced(AiModelConfig config) {
        if (agentRepository.existsByModelConfigId(config.getId())) {
            throw new BusinessException(ResultCode.RESOURCE_IN_USE,
                    "模型配置 [" + config.getDisplayNameOrDefault() + "] 正被智能体引用，无法删除");
        }
        if (chatbotRepository.existsByModelConfigId(config.getId())) {
            throw new BusinessException(ResultCode.RESOURCE_IN_USE,
                    "模型配置 [" + config.getDisplayNameOrDefault() + "] 正被聊天机器人引用，无法删除");
        }
    }
}
