package com.sdtool.common.api;

import com.sdtool.common.base.BaseService;
import com.sdtool.common.datas.RedisKeys;
import com.sdtool.common.entity.system.*;
import com.dySweetFishPlugin.redis.RedisService;
import com.sweetfish.convert.json.JsonConvert;
import com.sweetfish.util.AnyValue;
import com.sweetfish.util.AutoLoad;
import com.sweetfish.util.ResourceType;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by jlutt on 2021-03-11
 *
 * @author jlutt
 */
@AutoLoad(false)
//@Local
@ResourceType(ERPLockDataService.class)
public class ERPLockDataService extends BaseService {

    @Resource(name = "APP_HOME")
    String homePath;

    @Resource
    RedisService redisService;

    @Resource
    NoSqlKeysService keysService;

    @Resource
    LoginUserService userService;

    @Resource
    JsonConvert jsonConvert;

    private String lockDataScriptHash;

    private String hIncLockScriptHash;

    /**
     * 该方法必须是可以重复调用， 当reload时需要重复调用init方法
     * 远程模式下该方法会重载成空方法
     *
     * @param config 配置参数
     */
    @Override
    public void init(AnyValue config) {
        super.init(config);
    }

    /**
     * 进程退出时，调用Service销毁
     * 远程模式下该方法会重载成空方法
     *
     * @param config 配置参数
     */
    @Override
    public void destroy(AnyValue config) {
        super.destroy(config);
    }

    @Override
    public void start(AnyValue config) {
        Path path = Paths.get(homePath + File.separator + "conf" + File.separator + "redisscript" + File.separator + "lock_data.lua");
        this.lockDataScriptHash = redisService.scriptLoad(readUTF8File(path, false));

        Path path2 = Paths.get(homePath + File.separator + "conf" + File.separator + "redisscript" + File.separator + "hincrby_multiple.lua");
        this.hIncLockScriptHash = redisService.scriptLoad(readUTF8File(path2, false));

    }

    /**
     * 锁定数据
     *
     * @param businessName 业务名称
     * @param ids          待锁定ids字符串，用逗号分隔
     * @param lockUserId   操作人id
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 错误信息 为0表示锁定成功
     */
    @SuppressWarnings({"unused"})
    public DataLockRedisItem lockData(String businessName, String ids, long lockUserId, String dataSourceId, long supplierCode) {
        String redisKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_DATA, supplierCode, true) + businessName;
        String redisUserKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_USERDATA, supplierCode, true) + businessName;

        String evalJson = (String) redisService.evalsha(this.lockDataScriptHash,
                2,
                redisKey,
                redisUserKey,
                String.valueOf(lockUserId),
                "0",
                ids);

        if (StringUtils.isEmpty(evalJson)) {
            return new DataLockRedisItem(1, "操作锁定数据出错，请联系服务商");
        }

        return jsonConvert.convertFromO(DataLockRedisItem.class, evalJson);

    }

    /**
     * 解锁数据
     *
     * @param businessName 业务名称
     * @param ids          待解锁ids字符串，用逗号分隔
     * @param lockUserId   操作人id
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 错误信息 为0表示锁定成功
     */
    @SuppressWarnings({"unused"})
    public DataLockRedisItem unLockData(String businessName, String ids, long lockUserId, String dataSourceId, long supplierCode) {
        String redisKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_DATA, supplierCode, true) + businessName;
        String redisUserKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_USERDATA, supplierCode, true) + businessName;

        String evalJson = (String) redisService.evalsha(this.lockDataScriptHash,
                2,
                redisKey,
                redisUserKey,
                String.valueOf(lockUserId),
                "1",
                ids);

        if (StringUtils.isEmpty(evalJson)) {
            return new DataLockRedisItem(1, "操作解锁数据出错，请联系服务商");
        }

        return jsonConvert.convertFromO(DataLockRedisItem.class, evalJson);

    }

    /**
     * 获取数据锁定人
     *
     * @param businessName 业务名称
     * @param ids          查询数据id
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 锁定信息
     */
    @SuppressWarnings({"unused"})
    public List<DataLockPersonItem> getLockData(String businessName, String ids, String dataSourceId, long supplierCode) {
        String redisKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_DATA, supplierCode, true) + businessName;
        String[] idArray = ids.split(",");

        List<String> redisList = redisService.hmget(redisKey, idArray);

        if (idArray.length != redisList.size()) {
            return null;
        }

        List<DataLockPersonItem> personItemList = new ArrayList<>();

        for (int i = 0; i < redisList.size(); i++) {
            if (StringUtils.isNotBlank(redisList.get(i))) {
                LoginUser lockUser = userService.getRedisLoginUser(Long.parseLong(redisList.get(i)), supplierCode);
                if (lockUser != null) {
                    personItemList.add(new DataLockPersonItem(Long.parseLong(idArray[i]), lockUser.getId(), lockUser.getUserName()));
                } else {
                    personItemList.add(new DataLockPersonItem(Long.parseLong(idArray[i]), Long.parseLong(redisList.get(i)), "无效的用户"));
                }
            }
        }

        return personItemList;
    }

    /**
     * 获取用户锁定的数据
     *
     * @param businessName 业务名称
     * @param userId       操作用户id
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 锁定信息
     */
    public List<DataLockPersonItem> getUserLockData(String businessName, long userId, String dataSourceId, long supplierCode) {
        LoginUser lockUser = userService.getRedisLoginUser(userId, supplierCode);

        String redisKey = keysService.getRedisKey(RedisKeys.KEY_ERP_LOCK_USERDATA, supplierCode, true) + businessName;
        Map<String, String> redisMap = redisService.hscan(redisKey, "*-" + userId + "-0");
        return redisMap.keySet().stream()
                .map(s -> {
                    String[] keys = s.split("-");
                    if (lockUser == null) {
                        return new DataLockPersonItem(Long.parseLong(keys[0]), userId, "");
                    } else {
                        return new DataLockPersonItem(Long.parseLong(keys[0]), userId, lockUser.getUserName());
                    }
                })
                .sorted(Comparator.comparingLong(DataLockPersonItem::getIdData))
                .collect(Collectors.toList());
    }

    /**
     * 批量在redis hash添加id锁定数据
     * 理论上应该使用redis的bitmap功能完成，待研究，总觉得存储id有些不够数，虽然我们的id无法超过2^32
     *
     * @param ids          ids
     * @param redisKey     键值，不要用getRedisKey转换，直接用redisKey定义的常量即可
     * @param supplierCode 分表
     * @return 锁定信息
     */
    public RedisHLockInfo hMultipleLockAdd(String ids, String redisKey, long supplierCode) {
        List<RedisHLockItem> itemList = Arrays.stream(ids.split(",")).map((id) -> {
            RedisHLockItem item = new RedisHLockItem();
            item.setIncrNum(1);
            item.setNumField(String.valueOf(id));
            item.setNumKey(keysService.getRedisKey(redisKey, supplierCode));
            return item;
        }).collect(Collectors.toList());

        //扔给redis处理
        String json = (String) redisService.evalsha(this.hIncLockScriptHash,
                1,
                keysService.getRedisKey(redisKey, supplierCode),
                jsonConvert.convertTo(itemList));

        if (StringUtils.isEmpty(json)) {
            return null;
        } else {
            return jsonConvert.convertFrom(RedisHLockInfo.class, json);
        }
    }

    public void hMultipleLockDel(String ids, String redisKey, long supplierCode) {
        String[] idArray = ids.split(",");
        redisService.hdel(keysService.getRedisKey(redisKey, supplierCode), idArray);
    }

    public long hLockAdd(String id, String redisKey, long supplierCode) {
        return redisService.hincrBy(keysService.getRedisKey(redisKey, supplierCode), id, 1);
    }

    public void hLockDel(String id, String redisKey, long supplierCode) {
        redisService.hdel(keysService.getRedisKey(redisKey, supplierCode), id);
    }
}
