package com.sdtool.business.service;

import com.sdtool.business.dao.SystemDao;
import com.sdtool.common.api.ConfigService;
import com.sdtool.common.api.NoSqlKeysService;
import com.sdtool.common.base.BaseService;
import com.sdtool.common.datas.RedisKeys;
import com.sdtool.common.entity.base.InvokeCallParams;
import com.sdtool.common.entity.base.InvokeCallResult;
import com.sdtool.common.entity.site.ERPTokenUser;
import com.sdtool.common.entity.site.SiteContent;
import com.sdtool.common.entity.system.ConfigValue;
import com.sdtool.common.entity.system.PageTableColumn;
import com.sdtool.common.entity.system.PersonConfigValue;
import com.sdtool.common.tool.ERPUtils;
import com.dySweetFishPlugin.redis.RedisService;
import com.dySweetFishPlugin.sql.dao.TunaService;
import com.sweetfish.convert.json.JsonConvert;
import com.sweetfish.service.Local;
import com.sweetfish.service.RetResult;
import com.sweetfish.util.AnyValue;
import com.sweetfish.util.AutoLoad;
import com.sweetfish.util.ResourceType;
import org.apache.commons.lang3.StringUtils;
import org.rex.RMap;

import javax.annotation.Resource;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.sweetfish.net.Server.RESNAME_SERVER_ROOT;

@AutoLoad(false)
@Local
@ResourceType(ConfigService.class)
public class ConfigServiceImpl extends BaseService implements ConfigService {

    @Resource(name = "property.sysRunMode")
    private String sysRunMode;

    @Resource
    RedisService redisService;

    @Resource
    NoSqlKeysService keysService;

    @Resource
    JsonConvert jsonConvert;

    @Resource
    TunaService tunaService;

    @Resource(name = RESNAME_SERVER_ROOT)
    String webRootStr;

    private SystemDao systemDao;

    /**
     * 脚本中无法方便的跨脚本清除缓存，缓存工作放java里面处理
     * 网站内容只缓存文本内容
     */
    private final ConcurrentHashMap<String, SiteContent> siteContentCache = new ConcurrentHashMap<>();

    @Override
    public void init(AnyValue config) {
        super.init(config);
    }

    @Override
    public void destroy(AnyValue config) {
        super.destroy(config);
    }

    @Override
    public void start(AnyValue config) {
        systemDao = tunaService.generate(SystemDao.class);
    }

    //region 系统配置管理

    /**
     * 获取系统配置信息
     *
     * @param configKey 配置key
     * @return 配置信息
     */
    @Override
    public ConfigValue getRedisConfigValue(String configKey, String dataSourceId, long supplierCode) {
        String json = redisService.hget(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), configKey);

        if (StringUtils.isEmpty(json)) {
            return null;
        }

        return jsonConvert.convertFrom(ConfigValue.class, json);
    }

    /**
     * 获取配置的第一个参数值long
     *
     * @param configKey    配置key
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return long
     */
    public long getLongRedisConfigValue1(String configKey, String dataSourceId, long supplierCode) {
        String json = redisService.hget(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), configKey);

        if (StringUtils.isEmpty(json)) {
            return 0L;
        }

        ConfigValue value = jsonConvert.convertFrom(ConfigValue.class, json);
        try {
            return Long.parseLong(value.getConfigValue1());
        } catch (NumberFormatException e) {
            return 0L;
        }
    }

    public int getIntRedisConfigValue1(String configKey, long supplierCode) {
        String json = redisService.hget(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), configKey);

        if (StringUtils.isEmpty(json)) {
            return 0;
        }

        ConfigValue value = jsonConvert.convertFrom(ConfigValue.class, json);
        try {
            return Integer.parseInt(value.getConfigValue1());
        } catch (NumberFormatException e) {
            return 0;
        }
    }

    /**
     * 获取系统配置信息
     *
     * @param configKey    配置key
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 配置信息
     */
    public List<ConfigValue> getRedisConfigValueMutil(String configKey, String dataSourceId, long supplierCode) {
        String[] split = configKey.split(",");
        return getRedisConfigValueList(dataSourceId, supplierCode, split);
    }

    /**
     * 获取系统配置信息
     *
     * @param dataSourceId 分库
     * @return 配置信息列表
     */
    @Override
    public List<ConfigValue> getRedisConfigValueList(String dataSourceId, long supplierCode, String[] keys) {
        List<String> valueList = redisService.hmget(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), keys);

        return valueList.stream().map((x) -> (ConfigValue) jsonConvert.convertFrom(ConfigValue.class, x)).collect(Collectors.toList());
    }

    /**
     * 获取配置列表，从redis中直接获取，不分页
     *
     * @param searchData  查询条件
     * @param currentUser 操作人
     * @return 配置列表
     */
    @Override
    @SuppressWarnings("rawtypes")
    public RetResult<List<ConfigValue>> getConfigValueList(RMap searchData, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        Map<String, String> configMap = redisService.hgetAll(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode));
        List<ConfigValue> configValueList = new ArrayList<>();

        configMap.forEach((k, v) -> {
            ConfigValue configValue = jsonConvert.convertFrom(ConfigValue.class, v);
            boolean canAdd = true;

            if (ERPUtils.mapContainKey(searchData, "keyWord")) {
                String keyWord = searchData.getString("keyWord");
                if (!configValue.getConfigKey().toLowerCase().contains(keyWord.toLowerCase()) &&
                        !configValue.getConfigName().toLowerCase().contains(keyWord.toLowerCase())) {
                    canAdd = false;
                }
            }

            if (canAdd) {
                configValueList.add(configValue);
            }
        });

        configValueList.sort((v1, v2) -> v1.getConfigKey().compareToIgnoreCase(v2.getConfigKey()));

        return RetResult.<List<ConfigValue>>successT().result(configValueList);
    }

    /**
     * 增加配置项
     *
     * @param configValue 配置信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    public RetResult<ConfigValue> addConfigValue(ConfigValue configValue, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        if (StringUtils.isEmpty(configValue.getConfigKey())) {
            return RetResult.<ConfigValue>errorT().retinfo("请输入配置键名");
        }

        if (redisService.hexists(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), configValue.getConfigKey())) {
            return RetResult.<ConfigValue>errorT().retinfo("配置键名已存在");
        }

        configValue.setConfigType(1);

        redisService.hset(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), configValue.getConfigKey(), jsonConvert.convertTo(configValue));

        systemDao.addConfigValue(configValue, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 修改配置项
     *
     * @param configValue 配置信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    public RetResult<ConfigValue> updateConfigValue(ConfigValue configValue, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        //configKey不允许修改

        if (StringUtils.isEmpty(configValue.getConfigKey())) {
            return RetResult.<ConfigValue>errorT().retinfo("请输入配置键名");
        }

        ConfigValue redisConfig = getRedisConfigValue(configValue.getConfigKey(), dataSourceId, supplierCode);
        if (redisConfig == null) {
            return RetResult.<ConfigValue>errorT().retinfo("不存在的配置项");
        }

        redisConfig.setConfigName(configValue.getConfigName());
        redisConfig.setConfigValue1(configValue.getConfigValue1());
        redisConfig.setConfigValue2(configValue.getConfigValue2());
        redisConfig.setConfigValue3(configValue.getConfigValue3());
        redisConfig.setRemark(configValue.getRemark());

        redisService.hset(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), redisConfig.getConfigKey(), jsonConvert.convertTo(redisConfig));

        systemDao.updateConfigValue(redisConfig, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 批量修改配置项，只有key和值
     *
     * @param valueList 配置项列表
     */
    @Override
    public void updateConfigOnlyValue(List<ConfigValue> valueList, String dataSourceId, long supplierCode) {
        valueList.forEach((x) -> {
            ConfigValue redisConfig = getRedisConfigValue(x.getConfigKey(), dataSourceId, supplierCode);
            if (redisConfig != null) {
                redisConfig.setConfigValue1(x.getConfigValue1());
                redisService.hset(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), redisConfig.getConfigKey(), jsonConvert.convertTo(redisConfig));
            }
        });

        systemDao.updateBatchConfigValue(valueList, dataSourceId, supplierCode);

    }

    /**
     * 删除配置项
     *
     * @param configValue 配置信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    public RetResult<ConfigValue> removeConfigValue(ConfigValue configValue, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        if (StringUtils.isEmpty(configValue.getConfigKey())) {
            return RetResult.<ConfigValue>errorT().retinfo("请输入配置键名");
        }

        ConfigValue redisConfig = getRedisConfigValue(configValue.getConfigKey(), dataSourceId, supplierCode);
        if (redisConfig == null) {
            return RetResult.<ConfigValue>errorT().retinfo("不存在的配置项");
        }

        redisService.hdel(keysService.getRedisKey(RedisKeys.KEY_CONFIGVALUE, supplierCode), redisConfig.getConfigKey());

        systemDao.deleteConfigValue(redisConfig, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 获取网站相关配置信息
     *
     * @param currentUser 操作人
     * @return 网站相关配置信息列表
     */
    @Override
    public RetResult<Map<String, String>> getSiteInfoConfig(ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        Map<String, String> content = new LinkedHashMap<>();

        //获取当前所有配置值
        List<ConfigValue> valueList = getRedisConfigValueList(dataSourceId, supplierCode, RedisKeys.SUPPLIER_SITEINFO_CONFIGARR);
        valueList.forEach((x) -> {
            if (x != null) {
                content.put(x.getConfigKey(), x.getConfigValue1());
            }
        });
        content.put("SiteLogo", "/apis/" + supplierCode + "/logo.png");

        return RetResult.<Map<String, String>>successT().result(content);

    }

    /**
     * 保存网站相关的配置
     *
     * @param valueList 网站相关配置信息列表
     * @return 操作人
     */
    @Override
    public RetResult<ConfigValue> saveSiteInfoConfigValue(List<ConfigValue> valueList, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        updateConfigOnlyValue(valueList, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    //endregion

    //region 人员配置

    /**
     * 获取个人配置信息
     * 注意判断返回空值情况
     *
     * @param idUser       用户id
     * @param configKey    配置信息
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 配置信息
     */
    public PersonConfigValue getPersonConfigValue(long idUser, String configKey, String dataSourceId, long supplierCode) {
        String json = redisService.hget(keysService.getRedisKey(RedisKeys.KEY_ERP_PERSONCONFIG, supplierCode),
                idUser + "_" + configKey);
        if (StringUtils.isBlank(json)) {
            return null;
        }
        return jsonConvert.convertFromO(PersonConfigValue.class, json);
    }

    /**
     * 保持配置信息
     *
     * @param idUser       用户id
     * @param configKey    配置主键
     * @param configValue  配置值
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 操作信息
     */
    public int savePersonConfigValue(long idUser, String configKey, String configValue, String dataSourceId, long supplierCode) {
        PersonConfigValue personConfigValue = new PersonConfigValue(idUser, configKey, configValue);
        long iResult = redisService.hset(keysService.getRedisKey(RedisKeys.KEY_ERP_PERSONCONFIG, supplierCode),
                idUser + "_" + configKey,
                personConfigValue);
        if (iResult == 1) {
            systemDao.addPersonConfigValue(personConfigValue, dataSourceId, supplierCode);
        } else {
            systemDao.updatePersonConfigValue(personConfigValue, dataSourceId, supplierCode);
        }

        return 1;
    }

    //endregion

    //region 页面表格列

    /**
     * 保存页面列
     *
     * @param pageName        页面名称
     * @param tableColumnList 表格列列表
     * @param currentUser     操作人
     * @param dataSourceId    分库
     * @param supplierCode    分表
     * @return 操作信息
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public RetResult<String> savePageTableColumn(String pageName, List<PageTableColumn> tableColumnList, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        RMap deleteParams = new RMap();
        deleteParams.put("idUser", currentUser.getId());
        deleteParams.put("pageName", pageName);

        tableColumnList.forEach((x) -> x.setIdUser(currentUser.getId()));

        systemDao.savePageTableColumn(deleteParams, tableColumnList, dataSourceId, supplierCode);
        return RetResult.successT();
    }

    /**
     * 获取页面列
     *
     * @param pageName     页面名称
     * @param currentUser  操作人
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 操作信息
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public RetResult<List<PageTableColumn>> getPageTableColumn(String pageName, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        RMap getParams = new RMap();
        getParams.put("idUser", currentUser.getId());
        getParams.put("pageName", pageName);

        List<PageTableColumn> tableColumnList = systemDao.selectPageTableColumn(getParams, dataSourceId, supplierCode);

        return RetResult.<List<PageTableColumn>>successT().result(tableColumnList);
    }

    //endregion

    //region 网站内容缓存

    public SiteContent getCacheSiteContentInfo(long idContent, long supplierCode) {
        String key = idContent + "-" + supplierCode;

        return siteContentCache.computeIfAbsent(key, k -> {
            String fileName = webRootStr + File.separator +
                    "siteContent" + File.separator +
                    supplierCode + File.separator +
                    idContent + ".txt";
            Path contentPath = Paths.get(fileName);
            if (!Files.exists(contentPath)) {
                return new SiteContent(idContent, "");
            }
            return new SiteContent(idContent, readUTF8File(contentPath, false));
        });
    }

    public void removeCacheSiteContentInfo(long idContent, long supplierCode) {
        String key = idContent + "-" + supplierCode;
        siteContentCache.remove(key);
    }

    //endregion

    //region 常用配置获取
    //直接获取配置代码还要判断默认，下面简化下代码

    public int erpOrderManagerKind(long supplierCode) {
        return getIntRedisConfigValue1("ERPOrderManger", supplierCode);
    }

    /**
     * 判断是否商城后台跑了ERP
     *
     * @param supplierCode 分表
     * @return 0否 1是 2ERP运行
     */
    public int mallERP(long supplierCode) {
        //非商城模式运行的直接返回2，因为有些代码是ERP执行的，不能和商城的0混淆了
        if (!"Mall".equalsIgnoreCase(sysRunMode)) {
            return 2;
        }

        return getIntRedisConfigValue1("MallERP", supplierCode);
    }

    //endregion

    public RetResult<InvokeCallResult> callConfig(InvokeCallParams source, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        if (source == null) {
            return RetResult.<InvokeCallResult>errorT().retinfo("无效的参数信息");
        }

        if (StringUtils.isEmpty(source.getBusinessMethod())) {
            return RetResult.<InvokeCallResult>errorT().retinfo("无效的参数方法[businessMethod]信息");
        }

        source.setCurrentUser(currentUser);
        source.setDataSourceId(dataSourceId);
        source.setSupplierCode(supplierCode);

        return callScript(source.getBusinessMethod(), source, currentUser, dataSourceId, supplierCode);
    }
}
