package com.hzs.system.base.provider;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.hzs.common.core.constant.CacheConstants;
import com.hzs.common.core.constant.MagicNumberConstants;
import com.hzs.common.core.constant.SystemFieldConstants;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.EDelFlag;
import com.hzs.common.core.enums.ELanguages;
import com.hzs.common.core.enums.ETransactionKey;
import com.hzs.common.core.enums.EYesNo;
import com.hzs.common.core.service.RedisService;
import com.hzs.common.core.utils.ThreadUtils;
import com.hzs.common.core.utils.reflect.ReflectUtils;
import com.hzs.common.domain.system.base.BdCountry;
import com.hzs.common.domain.system.base.BdTransaction;
import com.hzs.common.domain.system.base.ext.TransactionParam;
import com.hzs.common.domain.system.base.ext.TransactionResult;
import com.hzs.system.base.ITransactionServiceApi;
import com.hzs.system.base.dto.TransactionDTO;
import com.hzs.system.base.service.IBdCountryService;
import com.hzs.system.base.service.IBdTransactionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 国际化翻译dubbo服务
 */
@Slf4j
@DubboService
public class TransactionServiceProvider implements ITransactionServiceApi {

    @Autowired
    private IBdTransactionService iBdTransactionService;
    @Autowired
    private IBdCountryService iBdCountryService;
    @Autowired
    private RedisService redisService;

    /**
     * 根据key读取数据库翻译
     *
     * @param fromCountry 来源国家（数据结算国）
     * @param content     翻译内容
     * @param toLocal     返回语言
     * @return
     */
    @Override
    public R<String> readTransactionByDataBase(Integer fromCountry, String content, String toLocal) {
        // 获取对应国家
        BdCountry bdCountry = iBdCountryService.getCountry(fromCountry);
        if (null == bdCountry) {
            return R.fail("国家不存在");
        }
        // 获取对应语言
        ELanguages fromLanguage = ELanguages.getLanguagesByValue(bdCountry.getLanguages());
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == fromLanguage || null == toLanguage) {
            return R.fail("语言不存在");
        }
        return R.ok(iBdTransactionService.queryTransactionContent(content, fromLanguage.getColumn(), toLanguage.getColumn()));
    }

    /**
     * 根据key列表读取数据库翻译
     *
     * @param fromCountry 来源国家（数据结算国）
     * @param contentList 翻译内容列表
     * @param toLocal     返回语言
     * @return
     */
    @Override
    public R<Map<String, String>> readTransactionListByDataBase(Integer fromCountry, Set<String> contentList, String toLocal) {
        // 获取对应国家
        BdCountry bdCountry = iBdCountryService.getCountry(fromCountry);
        if (null == bdCountry) {
            return R.fail("国家不存在");
        }
        // 获取对应语言
        ELanguages fromLanguage = ELanguages.getLanguagesByValue(bdCountry.getLanguages());
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == fromLanguage || null == toLanguage) {
            return R.fail("语言不存在");
        }
        return R.ok(this.queryTransactionListByDataBase(contentList, fromLanguage, toLanguage));
    }

    /**
     * 根据key读取缓存翻译
     *
     * @param key 翻译key（key规则 + 实际数据ID）
     * @return
     */
    @Override
    public R<String> readTransactionByCache(String key, String toLocal) {
        // 获取对应语言
        ELanguages language = ELanguages.getLanguagesByLocal(toLocal);
        if (null == language) {
            return R.fail("语言不存在");
        }
        return R.ok(redisService.getCacheMapValue(CacheConstants.LANGUAGE_KEY + language.getLocal(), key));
    }

    /**
     * 根据key列表读取缓存翻译
     *
     * @param keySet  翻译key列表（key规则 + 实际数据ID）
     * @param toLocal 返回语言
     * @return
     */
    @Override
    public R<Map<String, String>> readTransactionListByCache(Set<String> keySet, String toLocal) {
        // 获取对应语言
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == toLanguage) {
            return R.fail("语言不存在");
        }
        return R.ok(this.queryTransactionListByCache(keySet, toLanguage));
    }

    /**
     * 根据key前缀列表读取数据库翻译
     *
     * @param keyPrefixSet 翻译key前缀列表（EnumsPrefixConstants中的前缀）
     * @return
     */
    @Override
    public R<Map<Integer, String>> readTransactionListDataBase(Set<String> keyPrefixSet, String toLocal) {
        // 获取对应语言
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == toLanguage) {
            return R.fail("语言不存在");
        }

        // 返回翻译map
        Map<Integer, String> resultMap = new HashMap<>();
        List<TransactionResult> list = iBdTransactionService.queryTransactionListByKeyPrefix(keyPrefixSet, toLanguage.getColumn());
        if (CollectionUtil.isNotEmpty(list)) {
            for (TransactionResult transactionResult : list) {
                resultMap.put(transactionResult.getPkId(), transactionResult.getToContent());
            }
        }
        return R.ok(resultMap);
    }

    @Override
    public R<Map<Integer, String>> readTransactionListDataBase(Collection<Integer> pkIdList, String toLocal) {
        // 获取对应语言
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == toLanguage) {
            return R.fail("语言不存在");
        }

        // 返回翻译map
        Map<Integer, String> resultMap = new HashMap<>();
        List<TransactionResult> list = iBdTransactionService.queryTransactionListByPkId(pkIdList, toLanguage.getColumn());
        if (CollectionUtil.isNotEmpty(list)) {
            for (TransactionResult transactionResult : list) {
                resultMap.put(transactionResult.getPkId(), transactionResult.getToContent());
            }
        }
        return R.ok(resultMap);
    }

    /**
     * 数据库+缓存同时查询
     *
     * @param fromCountry 来源国家（数据结算国）
     * @param contentList 翻译内容列表
     * @param keyList     翻译key（key规则 + 实际数据ID）
     * @return
     */
    @Override
    public R<Map<String, String>> readTransactionAll(Integer fromCountry, Set<String> contentList, Set<String> keyList, String toLocal) {
        // 获取对应国家
        BdCountry bdCountry = iBdCountryService.getCountry(fromCountry);
        if (null == bdCountry) {
            return R.fail("国家不存在");
        }
        // 获取对应语言
        ELanguages fromLanguage = ELanguages.getLanguagesByValue(bdCountry.getLanguages());
        ELanguages toLanguage = ELanguages.getLanguagesByLocal(toLocal);
        if (null == fromLanguage || null == toLanguage) {
            return R.fail("语言不存在");
        }

        // 查询数据库翻译
        Future<Map<String, String>> dataBaseFuture = null;
        if (CollectionUtils.isNotEmpty(contentList)) {
            dataBaseFuture = ThreadUtils.threadPoolExecutor.submit(() -> this.queryTransactionListByDataBase(contentList, fromLanguage, toLanguage));
        }

        // 查询缓存翻译
        Future<Map<String, String>> cacheFuture = null;
        if (CollectionUtils.isNotEmpty(keyList)) {
            cacheFuture = ThreadUtils.threadPoolExecutor.submit(() -> this.queryTransactionListByCache(keyList, toLanguage));
        }

        // 需要返回的结果map
        Map<String, String> resultMap = new HashMap<>();
        try {
            // 将查询数据库和查询缓存的map进行合并后返回
            if (null != dataBaseFuture) {
                resultMap.putAll(dataBaseFuture.get());
            }
            if (null != cacheFuture) {
                resultMap.putAll(cacheFuture.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return R.ok(resultMap);
    }


    /**
     * 创建国际化翻译（只写入缓存）
     *
     * @param pkCountry 结算国
     * @param key       翻译key规则
     * @param content   翻译内容
     * @return
     */
    @Override
    public R<Integer> createTransactionCacheOnly(Integer pkCountry, ETransactionKey key, String content) {
        return this.createTransaction(pkCountry, key, content, EYesNo.YES, EYesNo.NO);
    }

    /**
     * 创建国际化翻译（写入缓存，上传OSS）
     *
     * @param pkCountry 结算国
     * @param key       翻译key规则
     * @param content   翻译内容
     * @return
     */
    @Override
    public R<Integer> createTransaction(Integer pkCountry, ETransactionKey key, String content) {
        return this.createTransaction(pkCountry, key, content, EYesNo.YES, EYesNo.YES);
    }

    /**
     * 创建国际化翻译
     *
     * @param pkCountry  结算国
     * @param key        翻译key规则
     * @param content    翻译内容
     * @param cacheFlag  是否写入缓存
     * @param uploadFlag 是否上传OSS
     * @return
     */
    @Override
    public R<Integer> createTransaction(Integer pkCountry, ETransactionKey key, String content, EYesNo cacheFlag, EYesNo uploadFlag) {
        // 获取对应国家
        BdCountry bdCountry = iBdCountryService.getCountry(pkCountry);
        if (null == bdCountry) {
            return R.fail("国家不存在");
        }
        // 获取对应语言
        ELanguages fromLanguage = ELanguages.getLanguagesByValue(bdCountry.getLanguages());
        if (null == fromLanguage) {
            return R.fail("语言不存在");
        }

        // 入库并返回
        TransactionParam transactionParam = new TransactionParam();
        transactionParam.setKey(key.getValue());
        transactionParam.setColumn(fromLanguage.getColumn());
        transactionParam.setColumnValue(content);
        transactionParam.setIsCache(null != cacheFlag ? cacheFlag.getIntValue() : EYesNo.YES.getIntValue());
        transactionParam.setIsUpload(null != uploadFlag ? uploadFlag.getIntValue() : EYesNo.YES.getIntValue());
        transactionParam.setPkCreator(MagicNumberConstants.PK_ADMIN);
        // 返回的翻译ID
        Integer pkId = iBdTransactionService.insertTransactionExt(transactionParam);

        if (null != pkId) {
            // 是否放入缓存进行处理
            if (null == cacheFlag || EYesNo.YES.getIntValue() == cacheFlag.getIntValue()) {
                redisService.setCacheMapValue(CacheConstants.LANGUAGE_KEY + fromLanguage.getLocal(), key.getValue() + pkId, content);
            }
            return R.ok(pkId);
        }
        return R.fail("国际化翻译处理失败");
    }

    @Override
    public R<Boolean> removeTransactionByPkId(List<Long> pkIdList) {
        List<BdTransaction> transactionList = iBdTransactionService.listByIds(pkIdList);
        if (CollectionUtil.isNotEmpty(transactionList)) {
            // 删除翻译并清除缓存
            this.removeTransaction(transactionList, true);
        }
        return R.ok();
    }

    @Override
    public R<Boolean> removeTransactionByKey(List<String> keyList) {
        QueryWrapper<BdTransaction> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("KEY", keyList);
        List<BdTransaction> transactionList = iBdTransactionService.list(queryWrapper);
        if (CollectionUtil.isNotEmpty(transactionList)) {
            // 删除翻译并清除缓存
            this.removeTransaction(transactionList, true);
        }
        return R.ok();
    }

    /**
     * 删除翻译并清除缓存
     *
     * @param transactionList 翻译实体列表
     * @param removeCache     是否清除缓存
     */
    private void removeTransaction(List<BdTransaction> transactionList, boolean removeCache) {
        // 删除翻译
        UpdateWrapper<BdTransaction> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in(SystemFieldConstants.PK_ID, transactionList.stream().map(BdTransaction::getPkId).collect(Collectors.toList()));
        updateWrapper.set(SystemFieldConstants.DEL_FLAG, EDelFlag.DELETE.getValue());
        updateWrapper.set(SystemFieldConstants.PK_MODIFIED, MagicNumberConstants.PK_ADMIN);
        updateWrapper.set(SystemFieldConstants.MODIFIED_TIME, new Date());
        iBdTransactionService.update(updateWrapper);

        if (removeCache) {
            // 判断是否需要删除缓存
            Object[] keyList = transactionList.stream().map(BdTransaction::getKey).toArray();
            // 根据目前语种枚举，生成对应数据集合
            for (ELanguages eLanguages : ELanguages.values()) {
                redisService.deleteMapObjectArr(CacheConstants.LANGUAGE_KEY + eLanguages.getLocal(), keyList);
            }
        }
    }

    /**
     * 根据key列表查询数据库
     *
     * @param contentList
     * @param fromLanguage
     * @param toLanguage
     * @return
     */
    private Map<String, String> queryTransactionListByDataBase(Set<String> contentList, ELanguages fromLanguage, ELanguages toLanguage) {
        // 后面需要看情况考虑是否超过1000条，适配ORACLE进行分页处理
        // 批量查询翻译内容
        List<TransactionResult> transactionList = iBdTransactionService.queryTransactionContentList(contentList, fromLanguage.getColumn(), toLanguage.getColumn());

        // 处理需要返回的结果map
        final Map<String, String> resultMap = new HashMap<>(transactionList.size());
        transactionList.parallelStream().forEach((transactionResult) -> resultMap.put(transactionResult.getFromContent(), transactionResult.getToContent()));

        return resultMap;
    }

    /**
     * 根据key列表查询缓存翻译
     *
     * @param keyList
     * @param toLanguage
     * @return
     */
    private Map<String, String> queryTransactionListByCache(Set<String> keyList, ELanguages toLanguage) {
        // 因目前工具中每次创建操作对象，此处提取到方法内创建一次，以便后续使用可以不用重复创建
        final HashOperations<String, String, String> hashOperations = redisService.getRedisHashOperations();

        // 处理需要返回的结果map
        final Map<String, String> resultMap = new HashMap<>(keyList.size());
        keyList.parallelStream().forEach((key) -> {
            String cacheContent = hashOperations.get(CacheConstants.LANGUAGE_KEY + toLanguage.getLocal(), key);
            if (null == cacheContent) {
                // 缓存中没有翻译，再查一下库（目前无缓存内容比较少，此处直接每个KEY查库）
                TransactionResult transactionResult = iBdTransactionService.queryTransactionKey(key, toLanguage.getColumn());
                if (null != transactionResult) {
                    resultMap.put(key, transactionResult.getToContent());
                } else {
                    // 如果数据库也没有，则默认为null
                    resultMap.put(key, null);
                }
            } else {
                resultMap.put(key, cacheContent);
            }
        });

        return resultMap;
    }


    @Override
    public R<Boolean> updateTransaction(Integer pkCountry, String content, Integer pkId, Boolean updateOther) {
        BdTransaction transaction = iBdTransactionService.getById(pkId);
        if (null == transaction) {
            return R.fail("翻译内容不存在");
        }

        log.info("==========更新翻译传入数据: pkId: {}, content: {}", pkId, content);
        log.info("==========更新翻译查询数据：{}", JSONUtil.toJsonStr(transaction));


        // 获取对应国家
        BdCountry bdCountry = iBdCountryService.getCountry(pkCountry);
        if (null == bdCountry) {
            return R.fail("国家不存在");
        }
        // 获取对应语言
        ELanguages fromLanguage = ELanguages.getLanguagesByValue(bdCountry.getLanguages());
        if (null == fromLanguage) {
            return R.fail("语言不存在");
        }

        // 入库并返回
        TransactionParam transactionParam = new TransactionParam();
        transactionParam.setPkId(pkId);
        transactionParam.setKey(transaction.getKey());
        transactionParam.setColumn(fromLanguage.getColumn());
        transactionParam.setColumnValue(content);
        transactionParam.setIsCache(transaction.getIsCache());
        transactionParam.setPkModified(MagicNumberConstants.PK_ADMIN);

        // 更新内容
        if (null != iBdTransactionService.updateTransactionExt(transactionParam, updateOther)) {
            redisService.setCacheMapValue(CacheConstants.LANGUAGE_KEY + fromLanguage.getLocal(), transaction.getKey(), content);
        }

        return R.ok();
    }


    @Override
    public R<Boolean> updateTransactionSync(TransactionDTO transactionDTO) {
        if (null == transactionDTO.getPkId()) {
            return R.fail("缺少翻译主键");
        }
        BdTransaction transaction = BeanUtil.copyProperties(transactionDTO, BdTransaction.class);
        transaction.setPkModified(MagicNumberConstants.PK_ADMIN);
        transaction.setModifiedTime(new Date());

        if (iBdTransactionService.updateById(transaction)) {
            // 更新成功
            transaction = iBdTransactionService.getById(transactionDTO.getPkId());

            // 判断是否需要更新缓存
            if (EYesNo.YES.getIntValue() == transaction.getIsCache()) {
                // 根据目前语种枚举，生成对应数据集合
                for (ELanguages eLanguages : ELanguages.values()) {
                    redisService.setCacheMapValue(CacheConstants.LANGUAGE_KEY + eLanguages.getLocal(), transaction.getKey(), ReflectUtils.invokeGetter(transaction, eLanguages.getColumn()));
                }
            }
            return R.ok();
        }
        return R.fail();
    }

}
