package com.yeejoin.amos.api.openapi.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.google.common.collect.Maps;
import com.yeejoin.amos.api.openapi.constant.Constant;
import com.yeejoin.amos.api.openapi.face.builder.UniqueFieldsQueryBuilder;
import com.yeejoin.amos.api.openapi.face.model.BizTokenModel;
import com.yeejoin.amos.api.openapi.util.MultiFieldKey;
import org.apache.ibatis.session.SqlSession;
import org.hibernate.service.spi.ServiceException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.typroject.tyboot.component.cache.Redis;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import static org.springframework.jdbc.object.BatchSqlUpdate.DEFAULT_BATCH_SIZE;

public class MyBaseServiceImpl<V, T, M extends BaseMapper<T>> extends BaseService<V, T, M> {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据指定唯一键批量保存或更新
     * @param entityList 需要保存或更新实体列表
     * @param lambdaQueryFunction 唯一值更新时函数
     * @param uniqueFieldFunction 生成唯一值的函数
     * @param fieldNames 需要作为唯一值的字段
     * @return 执行成功返回true
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdateBatchByUniqueFields(
            List<T> entityList,
            Function<T, LambdaQueryWrapper<T>> lambdaQueryFunction,
            Function<T, MultiFieldKey> uniqueFieldFunction,
            String... fieldNames) {
        Assert.notEmpty(entityList, "error: entityList must not be empty");
        Assert.notEmpty(fieldNames, "error: fieldNames must not be empty");

        Class<?> cls = currentModelClass();
        TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
        Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");

        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            // 构建一个Map，用于存储已存在的实体，键为MultiFieldKey，值为实体本身
            Map<MultiFieldKey, T> existingEntityMap = new HashMap<>();

//            List<String> fieldNameList = Arrays.asList(fieldNames);
//            Map<String, Integer> fieldNameIndexMap = IntStream.range(0, fieldNames.length)
//                    .boxed()
//                    .collect(Collectors.toMap(fieldNameList::get, Function.identity()));

            // 构建查询条件，根据fieldNames动态生成
//            for (String fieldName : fieldNames) {
//                int index = fieldNameIndexMap.get(fieldName);
//                queryWrapper.in(fieldName, entityList.stream()
//                        .map(entity -> uniqueFieldFunction.apply(entity).getField(index))
//                        .collect(Collectors.toList()));
//            }

            QueryWrapper<T> queryWrapper = new QueryWrapper<>();
            // 构建查询条件-根据传入entityList取出所有唯一字段作为查询条件
            new UniqueFieldsQueryBuilder().buildQueryWrapper(queryWrapper, entityList);
            // 预先查询所有可能存在的实体
            List<T> existingEntities = baseMapper.selectList(queryWrapper);

            for (T existingEntity : existingEntities) {
                MultiFieldKey key = createKeyFromEntity(existingEntity, fieldNames);
                existingEntityMap.put(key, existingEntity);
            }

            int i = 0;
            for (T entity : entityList) {
                MultiFieldKey uniqueKey = uniqueFieldFunction.apply(entity);
                T target = existingEntityMap.get(uniqueKey);

                if (Objects.isNull(target)) {
                    batchSqlSession.insert(sqlStatement(SqlMethod.INSERT_ONE), entity);
                } else {
                    Map<String, Object> param = Maps.newHashMap();
                    param.put(Constants.WRAPPER, lambdaQueryFunction.apply(entity));
                    param.put(Constants.ENTITY, entity);
                    // 更新操作
                    batchSqlSession.update(sqlStatement(SqlMethod.UPDATE), param);
                }

                if (++i % DEFAULT_BATCH_SIZE == 0) {
                    batchSqlSession.flushStatements();
                }
            }
            batchSqlSession.flushStatements();
        } catch (Exception e) {
            log.error("Error during batch save or update", e);
            throw new ServiceException("Error during batch save or update", e);
        }

        return true;
    }

    // 辅助方法，从实体中根据字段名创建MultiFieldKey
    @Contract("_, _ -> new")
    private @NotNull MultiFieldKey createKeyFromEntity(T entity, String[] fieldNames) {
        Object[] values = new Object[fieldNames.length];
        for (int i = 0; i < fieldNames.length; i++) {
            values[i] = ReflectionKit.getMethodValue(entity.getClass(), entity, fieldNames[i]);
        }
        return new MultiFieldKey(values);
    }

    public String getAppId() {
        String tokenKey = Redis.genKey(Constant.TOKEN_PREFIX, RequestContext.getToken());
        BizTokenModel bizTokenModel = (BizTokenModel) redisTemplate.opsForValue().get(tokenKey);
        return bizTokenModel.getAppId();
    }
}
