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

import com.infoepoch.pms.agent.config.JsonUtils;
import com.infoepoch.pms.agent.common.utils.SnowFlake;
import com.infoepoch.pms.agent.platform.shared.exception.BusinessException;
import com.infoepoch.pms.agent.platform.shared.response.ResultCode;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * AI 模型配置实体。
 * <p>
 * 代表某供应商下的一个具体模型（如 qwen-max、text-embedding-v3），
 * 包含该模型的参数上限和默认推理参数。
 * 模型类型分为 LLM（语言模型）、EMBEDDING（向量化模型）和 RERANK（重排模型）三类。
 * </p>
 */
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AiModelConfig {

    /**
     * 模型名称最大长度。
     */
    private static final int MODEL_NAME_MAX_LENGTH = 100;
    /**
     * 显示名称最大长度。
     */
    private static final int DISPLAY_NAME_MAX_LENGTH = 100;

    /**
     * 主键。
     */
    private String id;

    /**
     * 所属供应商（多对一，懒加载）。
     */
    private AiModelProvider provider;

    /**
     * 所属供应商id
     */
    private String providerId;

    /**
     * 模型名称，如 qwen-max、gpt-4o。
     */
    private String modelName;

    /**
     * 模型类型：LLM 或 EMBEDDING。
     */
    private String modelType;

    /**
     * 模型显示名称，用于 UI 展示，可选。
     */
    private String displayName;

    /**
     * 最大 Token 数，可选。
     */
    private Integer maxTokens;

    /**
     * 默认温度参数，可选。
     */
    private BigDecimal temperature;

    /**
     * 上下文窗口大小，可选。
     */
    private Integer contextWindow;

    /**
     * 向量维度（仅 EMBEDDING 类型有效），可选。
     */
    private Integer vectorDimension;

    /**
     * 是否启用 thinking（仅 LLM 类型有效），可选。
     */
    private Boolean enableThinking;

    /**
     * 是否启用多模态（仅 LLM 类型有效），可选。
     */
    private Boolean multiModel;

    /**
     * 自定义扩展配置，JSON 格式，可选。
     */
    private Map<String, Object> customConfig;

    /**
     * 自定义扩展配置json
     */
    private String customConfigJson;

    /**
     * 是否启用。
     */
    private boolean active = true;

    /**
     * 创建人 ID，预留字段。
     */
    private String createdUserId;

    /**
     * 最后更新人 ID，预留字段。
     */
    private String updatedUserId;

    /**
     * 记录创建时间。
     */
    private LocalDateTime createdAt;

    /**
     * 记录最后更新时间。
     */
    private LocalDateTime updatedAt;

    /**
     * 仓储还原
     */
    public AiModelConfig(String id, String providerId, String modelName, String modelType, String displayName,
                         Integer maxTokens, BigDecimal temperature, Integer contextWindow, Integer vectorDimension,
                         Boolean enableThinking, Boolean multiModel, String customConfigJson, boolean active,
                         String createdUserId, String updatedUserId, LocalDateTime createdAt, LocalDateTime updatedAt) {
        this.id = id;
        this.providerId = providerId;
        this.modelName = modelName;
        this.modelType = modelType;
        this.displayName = displayName;
        this.maxTokens = maxTokens;
        this.temperature = temperature;
        this.contextWindow = contextWindow;
        this.vectorDimension = vectorDimension;
        this.enableThinking = enableThinking;
        this.multiModel = multiModel;
        this.customConfigJson = customConfigJson;
        this.active = active;
        this.createdUserId = createdUserId;
        this.updatedUserId = updatedUserId;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
        //还原自定义配置
        if (StringUtils.hasText(customConfigJson)) {
            customConfig = JsonUtils.jsonToMap(customConfigJson);
        }
    }

    /**
     * 创建模型配置。
     *
     * @param provider        所属供应商，不能为 null
     * @param modelName       模型名称，必填，最长 100 字符
     * @param modelType       模型类型（LLM/EMBEDDING），必填
     * @param displayName     显示名称，可选，最长 100 字符
     * @param maxTokens       最大 Token 数，可选
     * @param temperature     温度参数，可选
     * @param contextWindow   上下文窗口，可选
     * @param vectorDimension 向量维度，可选
     * @param enableThinking  是否启用 thinking，可选
     * @param multiModel      是否启用多模态，可选
     * @param customConfig    自定义配置，可选
     */
    public AiModelConfig(AiModelProvider provider, String modelName, String modelType,
                         String displayName, Integer maxTokens, BigDecimal temperature,
                         Integer contextWindow, Integer vectorDimension,
                         Boolean enableThinking, Boolean multiModel,
                         Map<String, Object> customConfig) {
        if (provider == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "供应商不能为空");
        }
        LocalDateTime now = LocalDateTime.now();
        this.id = SnowFlake.instant().nextId().toString();
        this.provider = provider;
        this.providerId = provider.getId();
        this.modelName = normalizeModelName(modelName);
        this.modelType = normalizeModelType(modelType);
        this.displayName = normalizeOptional(displayName, DISPLAY_NAME_MAX_LENGTH, "显示名称");
        this.maxTokens = normalizeLlmField(this.modelType, maxTokens);
        this.temperature = normalizeLlmField(this.modelType, temperature);
        this.contextWindow = normalizeLlmField(this.modelType, contextWindow);
        this.vectorDimension = normalizeEmbeddingField(this.modelType, vectorDimension);
        this.enableThinking = normalizeLlmField(this.modelType, enableThinking);
        this.multiModel = normalizeLlmField(this.modelType, multiModel);
        this.customConfig = customConfig;
        this.customConfigJson = JsonUtils.objectToJson(customConfig);
        this.createdAt = now;
        this.updatedAt = now;
    }

    public static AiModelConfig restore(String id,
                                        AiModelProvider provider,
                                        String modelName,
                                        String modelType,
                                        String displayName,
                                        Integer maxTokens,
                                        BigDecimal temperature,
                                        Integer contextWindow,
                                        Integer vectorDimension,
                                        Boolean enableThinking,
                                        Boolean multiModel,
                                        Map<String, Object> customConfig,
                                        boolean active,
                                        String createdUserId,
                                        String updatedUserId,
                                        LocalDateTime createdAt,
                                        LocalDateTime updatedAt) {
        AiModelConfig config = new AiModelConfig();
        config.id = id;
        config.provider = provider;
        config.providerId = provider == null ? null : provider.getId();
        config.modelName = modelName;
        config.modelType = modelType;
        config.displayName = displayName;
        config.maxTokens = maxTokens;
        config.temperature = temperature;
        config.contextWindow = contextWindow;
        config.vectorDimension = vectorDimension;
        config.enableThinking = enableThinking;
        config.multiModel = multiModel;
        config.customConfig = customConfig;
        config.customConfigJson = JsonUtils.objectToJson(customConfig);
        config.active = active;
        config.createdUserId = createdUserId;
        config.updatedUserId = updatedUserId;
        config.createdAt = createdAt;
        config.updatedAt = updatedAt;
        return config;
    }

    public void assignId(String id) {
        this.id = id;
    }

    public void setProvider(AiModelProvider provider) {
        this.provider = provider;
        this.providerId = provider == null ? null : provider.getId();
    }

    /**
     * 修改模型配置参数。
     *
     * @param modelName       新模型名称，必填，最长 100 字符
     * @param modelType       新模型类型（LLM/EMBEDDING），必填
     * @param displayName     新显示名称，可选，最长 100 字符
     * @param maxTokens       新最大 Token 数，可选
     * @param temperature     新温度参数，可选
     * @param contextWindow   新上下文窗口，可选
     * @param vectorDimension 新向量维度，可选
     * @param enableThinking  新 thinking 开关，可选
     * @param multiModel      新多模态开关，可选
     * @param customConfig    新自定义配置，可选
     */
    public void modify(String modelName, String modelType, String displayName,
                       Integer maxTokens, BigDecimal temperature, Integer contextWindow,
                       Integer vectorDimension, Boolean enableThinking, Boolean multiModel,
                       Map<String, Object> customConfig) {
        this.modelName = normalizeModelName(modelName);
        this.modelType = normalizeModelType(modelType);
        this.displayName = normalizeOptional(displayName, DISPLAY_NAME_MAX_LENGTH, "显示名称");
        this.maxTokens = normalizeLlmField(this.modelType, maxTokens);
        this.temperature = normalizeLlmField(this.modelType, temperature);
        this.contextWindow = normalizeLlmField(this.modelType, contextWindow);
        this.vectorDimension = normalizeEmbeddingField(this.modelType, vectorDimension);
        this.enableThinking = normalizeLlmField(this.modelType, enableThinking);
        this.multiModel = normalizeLlmField(this.modelType, multiModel);
        this.customConfig = customConfig;
        this.customConfigJson = JsonUtils.objectToJson(customConfig);
        this.updatedAt = LocalDateTime.now();
    }

    /**
     * 切换启用/禁用状态。
     */
    public void toggleActive() {
        this.active = !this.active;
        this.updatedAt = LocalDateTime.now();
    }

    /**
     * 规范化并校验模型名称。
     *
     * @param modelName 原始模型名称
     * @return 规范化后的模型名称
     */
    private String normalizeModelName(String modelName) {
        if (!StringUtils.hasText(modelName)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "模型名称不能为空");
        }
        String normalized = modelName.trim();
        if (normalized.length() > MODEL_NAME_MAX_LENGTH) {
            throw new BusinessException(ResultCode.PARAM_ERROR,
                    "模型名称长度不能超过 " + MODEL_NAME_MAX_LENGTH + " 个字符");
        }
        return normalized;
    }

    /**
     * 规范化并校验模型类型。
     *
     * @param modelType 原始模型类型
     * @return 规范化后的模型类型
     */
    private String normalizeModelType(String modelType) {
        if (!StringUtils.hasText(modelType)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "模型类型不能为空");
        }
        String normalized = modelType.trim().toUpperCase();
        if (!normalized.equals("LLM") && !normalized.equals("EMBEDDING") && !normalized.equals("RERANK")) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "模型类型必须是 LLM、EMBEDDING 或 RERANK");
        }
        return normalized;
    }

    private <T> T normalizeLlmField(String modelType, T value) {
        return "LLM".equals(modelType) ? value : null;
    }

    private <T> T normalizeEmbeddingField(String modelType, T value) {
        return "EMBEDDING".equals(modelType) ? value : null;
    }

    /**
     * 规范化并校验可选字符串字段。
     *
     * @param value     原始值
     * @param maxLength 最大长度
     * @param fieldName 字段名（用于异常提示）
     * @return 规范化结果，空字符串归一化为 null
     */
    private String normalizeOptional(String value, int maxLength, String fieldName) {
        if (value == null) {
            return null;
        }
        String normalized = value.trim();
        if (normalized.length() > maxLength) {
            throw new BusinessException(ResultCode.PARAM_ERROR,
                    fieldName + " 长度不能超过 " + maxLength + " 个字符");
        }
        return normalized.isEmpty() ? null : normalized;
    }

    public String getCustomConfigJson() {
        if (customConfig != null && !customConfig.isEmpty()){
            return JsonUtils.objectToJson(customConfig);
        }
        return customConfigJson;
    }

    public String getDisplayNameOrDefault() {
        return StringUtils.hasText(displayName) ? displayName : modelName;
    }
}
