package com.infoepoch.pms.agent.platform.model.adapter.out.persistence;

import com.infoepoch.pms.agent.common.utils.LogHelper;
import com.infoepoch.pms.agent.common.utils.OracleUtils;
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.AiModelConfigCriteria;
import com.infoepoch.pms.agent.platform.model.domain.irepository.IAiModelConfigRepository;
import com.infoepoch.pms.agent.platform.shared.adapter.out.crypto.AesEncryptConverter;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.infoepoch.pms.agent.platform.shared.exception.ValidationException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.sql.*;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * AI 模型配置表仓储实现
 */
@Repository
public class AiModelConfigRepository implements IAiModelConfigRepository {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 新增
     */
    @Override
    public boolean insert(AiModelConfig entity) {
        String sql = "INSERT INTO AI_MODEL_CONFIG(ID, PROVIDER_ID, MODEL_NAME, MODEL_TYPE, DISPLAY_NAME, MAX_TOKENS, TEMPERATURE, CONTEXT_WINDOW, VECTOR_DIMENSION, ENABLE_THINKING, MULTI_MODEL, CUSTOM_CONFIG, IS_ACTIVE, CREATED_USER_ID, UPDATED_USER_ID, CREATED_AT, UPDATED_AT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        int result;
        try {
            result = jdbcTemplate.update(sql, entity.getId(), entity.getProviderId(), entity.getModelName(),
                    entity.getModelType(), entity.getDisplayName(), entity.getMaxTokens(), entity.getTemperature(),
                    entity.getContextWindow(), entity.getVectorDimension(), entity.getEnableThinking(),
                    entity.getMultiModel(), entity.getCustomConfigJson(), entity.isActive(),
                    entity.getCreatedUserId(), entity.getUpdatedUserId(), entity.getCreatedAt() != null ? Timestamp.valueOf(entity.getCreatedAt()) : null, entity.getUpdatedAt() != null ? Timestamp.valueOf(entity.getUpdatedAt()) : null);
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            throw new ValidationException("新增 AI 模型配置表 数据 失败。");
        }
        return result > 0;
    }

    /**
     * 更新
     */
    @Override
    public boolean update(AiModelConfig entity) {
        String sql = "UPDATE AI_MODEL_CONFIG SET PROVIDER_ID = ?, MODEL_NAME = ?, MODEL_TYPE = ?, DISPLAY_NAME = ?, MAX_TOKENS = ?, TEMPERATURE = ?, CONTEXT_WINDOW = ?, VECTOR_DIMENSION = ?, ENABLE_THINKING = ?, MULTI_MODEL = ?, CUSTOM_CONFIG = ?, IS_ACTIVE = ?, CREATED_USER_ID = ?, UPDATED_USER_ID = ?, CREATED_AT = ?, UPDATED_AT = ? WHERE ID = ?";
        int result;
        try {
            result = jdbcTemplate.update(sql, entity.getProviderId(), entity.getModelName(), entity.getModelType(),
                    entity.getDisplayName(), entity.getMaxTokens(), entity.getTemperature(),
                    entity.getContextWindow(), entity.getVectorDimension(), entity.getEnableThinking(),
                    entity.getMultiModel(), entity.getCustomConfigJson(), entity.isActive(),
                    entity.getCreatedUserId(), entity.getUpdatedUserId(), entity.getCreatedAt() != null ? Timestamp.valueOf(entity.getCreatedAt()) : null,
                    entity.getUpdatedAt() != null ? Timestamp.valueOf(entity.getUpdatedAt()) : null, entity.getId());
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            throw new ValidationException("更新 AI 模型配置表 数据 失败。");
        }
        return result >= 0;
    }

    /**
     * 批量新增
     */
    public int[] batchInsert(List<AiModelConfig> list) {
        String sql = "INSERT INTO AI_MODEL_CONFIG(ID, PROVIDER_ID, MODEL_NAME, MODEL_TYPE, DISPLAY_NAME, MAX_TOKENS, TEMPERATURE, CONTEXT_WINDOW, VECTOR_DIMENSION, ENABLE_THINKING, MULTI_MODEL, CUSTOM_CONFIG, IS_ACTIVE, CREATED_USER_ID, UPDATED_USER_ID, CREATED_AT, UPDATED_AT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        int[] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                int j = 0;
                AiModelConfig item = list.get(i);
                ps.setString(++j, item.getId());
                ps.setString(++j, item.getProviderId());
                ps.setString(++j, item.getModelName());
                ps.setString(++j, item.getModelType());
                ps.setString(++j, item.getDisplayName());
                ps.setObject(++j, item.getMaxTokens());
                ps.setBigDecimal(++j, item.getTemperature());
                ps.setObject(++j, item.getContextWindow());
                ps.setObject(++j, item.getVectorDimension());
                ps.setObject(++j, item.getEnableThinking());
                ps.setObject(++j, item.getMultiModel());
                ps.setString(++j, item.getCustomConfigJson());
                ps.setBoolean(++j, item.isActive());
                ps.setString(++j, item.getCreatedUserId());
                ps.setString(++j, item.getUpdatedUserId());
                ps.setTimestamp(++j, item.getCreatedAt() != null ? Timestamp.valueOf(item.getCreatedAt()): null);
                ps.setTimestamp(++j, item.getUpdatedAt() != null ? Timestamp.valueOf(item.getUpdatedAt()): null);
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        });
        return result;
    }

    /**
     * 批量更新
     */
    public int[] batchUpdate(List<AiModelConfig> list) {
        String sql = "UPDATE AI_MODEL_CONFIG SET PROVIDER_ID = ?, MODEL_NAME = ?, MODEL_TYPE = ?, DISPLAY_NAME = ?, MAX_TOKENS = ?, TEMPERATURE = ?, CONTEXT_WINDOW = ?, VECTOR_DIMENSION = ?, ENABLE_THINKING = ?, MULTI_MODEL = ?, CUSTOM_CONFIG = ?, IS_ACTIVE = ?, CREATED_USER_ID = ?, UPDATED_USER_ID = ?, CREATED_AT = ?, UPDATED_AT = ? WHERE ID = ? ";
        int[] result = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                int j = 0;
                AiModelConfig item = list.get(i);
                ps.setString(++j, item.getProviderId());
                ps.setString(++j, item.getModelName());
                ps.setString(++j, item.getModelType());
                ps.setString(++j, item.getDisplayName());
                ps.setObject(++j, item.getMaxTokens());
                ps.setBigDecimal(++j, item.getTemperature());
                ps.setObject(++j, item.getContextWindow());
                ps.setObject(++j, item.getVectorDimension());
                ps.setObject(++j, item.getEnableThinking());
                ps.setObject(++j, item.getMultiModel());
                ps.setString(++j, item.getCustomConfigJson());
                ps.setBoolean(++j, item.isActive());
                ps.setString(++j, item.getCreatedUserId());
                ps.setString(++j, item.getUpdatedUserId());
                ps.setTimestamp(++j, item.getCreatedAt() != null ? Timestamp.valueOf(item.getCreatedAt()) : null);
                ps.setTimestamp(++j, item.getUpdatedAt() != null ? Timestamp.valueOf(item.getUpdatedAt()) : null);
                ps.setString(++j, item.getId());
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        });
        return result;
    }

    /**
     * 删除
     */
    @Override
    public boolean delete(String id) {
        String sql = "DELETE FROM AI_MODEL_CONFIG WHERE ID = ?";
        int result;
        try {
            result = jdbcTemplate.update(sql, id);
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            throw new ValidationException("删除 AI 模型配置表 数据 失败。");
        }
        return result > 0;
    }

    /**
     * 根据Id查询
     */
    @Override
    public AiModelConfig selectById(String id) {
        String sql = "SELECT * FROM AI_MODEL_CONFIG WHERE ID = ? ";
        try {
            return jdbcTemplate.queryForObject(sql, new AiModelConfigRowMapper(), id);
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            return null;
        }
    }

    /**
     * 根据查询条件查询单个对象
     */
    @Override
    public AiModelConfig selectOneByCriteria(AiModelConfigCriteria criteria) {
        StringBuffer buffer = new StringBuffer("SELECT * FROM AI_MODEL_CONFIG ");
        List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria), 1, 1);
        try {
            return jdbcTemplate.queryForObject(buffer.toString(), list.toArray(), new AiModelConfigRowMapper());
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            return null;
        }
    }

    /**
     * 根据查询条件查询对象集合
     */
    @Override
    public List<AiModelConfig> selectByCriteria(AiModelConfigCriteria criteria) {
        StringBuffer buffer = new StringBuffer("SELECT * FROM AI_MODEL_CONFIG ");
        List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria));
        try {
            return jdbcTemplate.query(buffer.toString(), list.toArray(), new AiModelConfigRowMapper());
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            return null;
        }
    }

    /**
     * 根据查询条件分页查询对象结合
     */
    @Override
    public List<AiModelConfig> selectCriteriaByPage(AiModelConfigCriteria criteria, int pageNum, int pageSize) {
        StringBuffer buffer = new StringBuffer("SELECT * FROM AI_MODEL_CONFIG ");
        List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria), pageNum, pageSize);
        try {
            return jdbcTemplate.query(buffer.toString(), list.toArray(), new AiModelConfigRowMapper());
        } catch (Exception e) {
            LogHelper.error(this,this.getClass().getName(),e);
            return null;
        }
    }

    /**
     * 根据条件查询对象总记录数
     */
    @Override
    public Integer selectCountByCriteria(AiModelConfigCriteria criteria) {
        StringBuffer buffer = new StringBuffer("SELECT COUNT(*) FROM AI_MODEL_CONFIG ");
        List<Object> list = OracleUtils.combinationSql(buffer, createCriteriaSql(criteria));
        int count = jdbcTemplate.queryForObject(buffer.toString(), list.toArray(), int.class);
        return count;
    }

    @Override
    public Optional<AiModelConfig> findById(String id) {
        return Optional.ofNullable(id).map(this::selectById);
    }

    @Override
    public AiModelConfig save(AiModelConfig entity) {
        if (entity == null) {
            return null;
        }
        if (findById(entity.getId()).isPresent()) {
            update(entity);
        } else {
            insert(entity);
        }
        return entity;
    }

    @Override
    public boolean existsByProviderIdAndModelNameAndModelType(String providerId, String modelName, String modelType) {
        AiModelConfigCriteria criteria = new AiModelConfigCriteria();
        criteria.setProviderId(providerId);
        List<AiModelConfig> configs = selectByCriteria(criteria);
        if (configs == null) {
            return false;
        }
        return configs.stream()
                .anyMatch(config -> Objects.equals(config.getModelName(), modelName)
                        && Objects.equals(config.getModelType(), modelType));
    }

    @Override
    public boolean existsByProviderIdAndModelNameAndModelTypeAndIdNot(String providerId,
                                                                      String modelName,
                                                                      String modelType,
                                                                      String id) {
        AiModelConfigCriteria criteria = new AiModelConfigCriteria();
        criteria.setProviderId(providerId);
        List<AiModelConfig> configs = selectByCriteria(criteria);
        if (configs == null) {
            return false;
        }
        return configs.stream()
                .anyMatch(config -> !Objects.equals(config.getId(), id)
                        && Objects.equals(config.getModelName(), modelName)
                        && Objects.equals(config.getModelType(), modelType));
    }

    @Override
    public List<AiModelConfig> findAllByProviderIdOrderByCreatedAtDesc(String providerId) {
        AiModelConfigCriteria criteria = new AiModelConfigCriteria();
        criteria.setProviderId(providerId);
        List<AiModelConfig> configs = selectByCriteria(criteria);
        if (configs == null) {
            return List.of();
        }
        return configs.stream()
                .sorted((left, right) -> right.getCreatedAt().compareTo(left.getCreatedAt()))
                .toList();
    }

    @Override
    public List<AiModelConfig> findAllByProviderId(String providerId) {
        AiModelConfigCriteria criteria = new AiModelConfigCriteria();
        criteria.setProviderId(providerId);
        List<AiModelConfig> configs = selectByCriteria(criteria);
        return configs == null ? List.of() : configs;
    }

    /**
     * RowMapper
     */
    private class AiModelConfigRowMapper implements RowMapper<AiModelConfig> {
        @Override
        public AiModelConfig mapRow(ResultSet rs, int i) throws SQLException {
            return new AiModelConfig(
                    rs.getString("ID"),
                    rs.getString("PROVIDER_ID"),
                    rs.getString("MODEL_NAME"),
                    rs.getString("MODEL_TYPE"),
                    rs.getString("DISPLAY_NAME"),
                    rs.getObject("MAX_TOKENS") != null ? rs.getInt("MAX_TOKENS") : null,
                    rs.getBigDecimal("TEMPERATURE"),
                    rs.getObject("CONTEXT_WINDOW") != null ? rs.getInt("CONTEXT_WINDOW") : null,
                    rs.getObject("VECTOR_DIMENSION") != null ? rs.getInt("VECTOR_DIMENSION") : null,
                    rs.getObject("ENABLE_THINKING") != null ? rs.getBoolean("ENABLE_THINKING") : null,
                    rs.getObject("MULTI_MODEL") != null ? rs.getBoolean("MULTI_MODEL") : null,
                    rs.getString("CUSTOM_CONFIG"),
                    rs.getObject("IS_ACTIVE") != null ? rs.getBoolean("IS_ACTIVE") : null,
                    rs.getString("CREATED_USER_ID"),
                    rs.getString("UPDATED_USER_ID"),
                    rs.getTimestamp("CREATED_AT").toLocalDateTime(),
                    rs.getTimestamp("UPDATED_AT").toLocalDateTime()
            );
        }
    }

    /**
     * 创建查询条件
     */
    private Map<String, Object> createCriteriaSql(AiModelConfigCriteria criteria) {
        Map<String, Object> andMap = new LinkedHashMap<>();
        if (criteria == null)
            return andMap;
        //所属供应商名称
        if (criteria.byProviderId())
            andMap.put(criteria.getProviderId() == null ? " PROVIDER_ID IS NULL " : " PROVIDER_ID = ? ", criteria.getProviderId());
        //所属供应商名称（LIKE）
        if (criteria.byProviderIdContain())
            andMap.put(" PROVIDER_ID LIKE ? ", "%" + criteria.getProviderIdContain() + "%");
        //原始模型名称，如 qwen-max
        if (criteria.byModelName())
            andMap.put(criteria.getModelName() == null ? " MODEL_NAME IS NULL " : " MODEL_NAME = ? ", criteria.getModelName());
        //原始模型名称，如 qwen-max（LIKE）
        if (criteria.byModelNameContain())
            andMap.put(" MODEL_NAME LIKE ? ", "%" + criteria.getModelNameContain() + "%");
        //模型类型：LLM / EMBEDDING
        if (criteria.byModelType())
            andMap.put(criteria.getModelType() == null ? " MODEL_TYPE IS NULL " : " MODEL_TYPE = ? ", criteria.getModelType());
        //模型显示名称
        if (criteria.byDisplayName())
            andMap.put(criteria.getDisplayName() == null ? " DISPLAY_NAME IS NULL " : " DISPLAY_NAME = ? ", criteria.getDisplayName());
        //模型显示名称（LIKE）
        if (criteria.byDisplayNameContain())
            andMap.put(" DISPLAY_NAME LIKE ? ", "%" + criteria.getDisplayNameContain() + "%");
        //是否启用
        if (criteria.byActive())
            andMap.put(criteria.getActive() == null ? " IS_ACTIVE IS NULL " : " IS_ACTIVE = ? ", criteria.getActive());
        return andMap;
    }

}
