package com.red.circle.other.infra.database.cache.service.other.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyBoxAwardDetailsConfigCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyBoxFortuneValueConfigCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyBoxGiftConfigDetailsCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyBoxInventoryCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyBoxTimeConfigCache;
import com.red.circle.other.infra.database.cache.key.GameNewLuckyBoxKeys;
import com.red.circle.other.infra.database.cache.service.other.GameNewLuckyBoxCacheService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.func.VoidCallable;
import com.red.circle.tool.core.json.JacksonUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

/**
 * 新 LuckyBox 缓存 实现
 *
 * @author zongpubin on 2023/8/26
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class GameNewLuckyBoxCacheServiceImpl implements GameNewLuckyBoxCacheService {

  private final RedisService redisService;

  // 记录每日抽中目标礼物次数
  @Override
  public void recordLuckyBoxAwardDay(Long userId, Integer count) {

    if (count <= 0 || Objects.isNull(userId)) {
      return;
    }

    String key = "LUCKY_BOX_DAY_" + ZonedDateTimeAsiaRiyadhUtils.nowDateToInt() + "_" + userId;
    redisService.increment(key, count, 7, TimeUnit.DAYS);
  }

  // 获取每日抽中目标礼物次数
  @Override
  public Integer getLuckyBoxAwardDay(Long userId) {

    if (Objects.isNull(userId)) {
      return 0;
    }

    String key = "LUCKY_BOX_DAY_" + ZonedDateTimeAsiaRiyadhUtils.nowDateToInt() + "_" + userId;
    return Optional.ofNullable(redisService.getStringToInteger(key)).orElse(0);
  }


  // 记录每周抽中目标礼物次数
  @Override
  public void recordLuckyBoxAwardWeek(Long userId, Integer count) {

    if (count <= 0 || Objects.isNull(userId)) {
      return;
    }

    String key =
        "LUCKY_BOX_WEEK_" + ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt() + "_" + userId;
    redisService.increment(key, count, 15, TimeUnit.DAYS);
  }

  // 获取每周抽中目标礼物次数
  @Override
  public Integer getLuckyBoxAwardWeek(Long userId) {

    if (Objects.isNull(userId)) {
      return 0;
    }

    String key =
        "LUCKY_BOX_WEEK_" + ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt() + "_" + userId;
    return Optional.ofNullable(redisService.getStringToInteger(key)).orElse(0);
  }

  // 创建每天的领取记录
  @Override
  public void receiveLuckyBoxAwardDay(Long userId, Integer number) {

    String key =
        "LUCKY_BOX_DAY_RECEIVE_" + ZonedDateTimeAsiaRiyadhUtils.nowDateToInt() + "_" + userId;

    String idsStr = redisService.getString(key);
    List<Integer> receiveList = Optional.ofNullable(JacksonUtils.readValue(idsStr, new
            TypeReference<List<Integer>>() {
            }))
        .orElse(Lists.newArrayList());
    if (receiveList.contains(number)) {
      return;
    }
    receiveList.add(number);
    redisService.setString(key, JacksonUtils.toJson(receiveList), 7, TimeUnit.DAYS);
  }

  // 获取每天的领取记录
  @Override
  public List<Integer> getReceiveLuckyBoxAwardDay(Long userId) {

    // for obj = 1,  obj= 2
    // {
    // receiveList.contains( obj.number)
    // }

    String key =
        "LUCKY_BOX_DAY_RECEIVE_" + ZonedDateTimeAsiaRiyadhUtils.nowDateToInt() + "_" + userId;
    String idsStr = redisService.getString(key);
    return Optional.ofNullable(JacksonUtils.readValue(idsStr, new
            TypeReference<List<Integer>>() {
            }))
        .orElse(Lists.newArrayList());
  }

  // 创建每周的领取记录
  @Override
  public void receiveLuckyBoxAwardWeek(Long userId, Integer number) {

    String key = "LUCKY_BOX_WEEK_RECEIVE_" + ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt() + "_"
        + userId;

    String idsStr = redisService.getString(key);
    List<Integer> receiveList = Optional.ofNullable(JacksonUtils.readValue(idsStr, new
            TypeReference<List<Integer>>() {
            }))
        .orElse(Lists.newArrayList());

    //
    if (receiveList.contains(number)) {
      return;
    }
    receiveList.add(number);
    redisService.setString(key, JacksonUtils.toJson(receiveList), 7, TimeUnit.DAYS);
  }

  // 获取每周的领取记录
  @Override
  public List<Integer> getReceiveLuckyBoxAwardWeek(Long userId) {

    String key = "LUCKY_BOX_WEEK_RECEIVE_" + ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt() + "_"
        + userId;
    String idsStr = redisService.getString(key);
    return Optional.ofNullable(JacksonUtils.readValue(idsStr, new
            TypeReference<List<Integer>>() {
            }))
        .orElse(Lists.newArrayList());
  }

  @Override
  public List<GameLuckyBoxTimeConfigCache> getTimeConfigCache(Long standardId, String sysOrigin,
      VoidCallable<List<GameLuckyBoxTimeConfigCache>> orElsePut) {
    return Optional
        .ofNullable(redisService.hashGet(getGameLuckyBoxTimeConfigKey(standardId), sysOrigin,
            new TypeReference<List<GameLuckyBoxTimeConfigCache>>() {
            }))
        .map(gameLuckyBoxTimeCaches -> gameLuckyBoxTimeCaches.stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList()))
        .orElseGet(() -> {
          List<GameLuckyBoxTimeConfigCache> gameLuckyBoxTimeConfigCaches = orElsePut.apply();
          if (CollectionUtils.isNotEmpty(gameLuckyBoxTimeConfigCaches)) {
            removeTimeConfigCache(standardId, sysOrigin);
            redisService
                .hashPut(getGameLuckyBoxTimeConfigKey(standardId), sysOrigin
                    , gameLuckyBoxTimeConfigCaches);
          }
          return gameLuckyBoxTimeConfigCaches;
        });
  }

  @Override
  public void removeTimeConfigCache(Long standardId, String sysOrigin) {
    if (Objects.isNull(standardId)) {
      return;
    }
    redisService.hashDelete(getGameLuckyBoxTimeConfigKey(standardId), sysOrigin);
  }

  @Override
  public List<GameLuckyBoxGiftConfigDetailsCache> getGiftInfoCache(Long standardId,
      String sysOrigin, VoidCallable<List<GameLuckyBoxGiftConfigDetailsCache>> orElsePut) {
    return Optional
        .ofNullable(redisService
            .hashGet(getGameLuckyBoxGiftConfigDetailsKey(standardId), sysOrigin,
                new TypeReference<List<GameLuckyBoxGiftConfigDetailsCache>>() {
                }))
        .map(gameLuckyBoxGiftCaches -> gameLuckyBoxGiftCaches.stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList()))
        .orElseGet(() -> {
          List<GameLuckyBoxGiftConfigDetailsCache> gameLuckyBoxGiftConfigDetailsCaches = orElsePut.apply();
          if (CollectionUtils.isNotEmpty(gameLuckyBoxGiftConfigDetailsCaches)) {
            removeGiftInfoCache(standardId, sysOrigin);
            redisService
                .hashPut(getGameLuckyBoxGiftConfigDetailsKey(standardId), sysOrigin
                    , gameLuckyBoxGiftConfigDetailsCaches);
          }
          return gameLuckyBoxGiftConfigDetailsCaches;
        });
  }

  @Override
  public void removeGiftInfoCache(Long standardId, String sysOrigin) {
    if (Objects.isNull(standardId)) {
      return;
    }
    redisService.hashDelete(getGameLuckyBoxGiftConfigDetailsKey(standardId),
        sysOrigin);
  }

  @Override
  public List<GameLuckyBoxAwardDetailsConfigCache> getAwardDetailsCache(Long awardId,
      String lotteryType, VoidCallable<List<GameLuckyBoxAwardDetailsConfigCache>> orElsePut) {
    return Optional.ofNullable(redisService.hashGet(
        getGameLuckyBoxAwardConfigDetailsKey(awardId), lotteryType,
        new TypeReference<List<GameLuckyBoxAwardDetailsConfigCache>>() {
        })).map(
        gameLuckyBoxAwardDetailsConfigCaches -> gameLuckyBoxAwardDetailsConfigCaches.stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList())).orElseGet(() -> {
      List<GameLuckyBoxAwardDetailsConfigCache> awardDetailsConfigCaches = orElsePut.apply();

      if (CollectionUtils.isNotEmpty(awardDetailsConfigCaches)) {
        removeAwardDetailsConfigCache(awardId, lotteryType);
        redisService.hashPut(
            getGameLuckyBoxAwardConfigDetailsKey(awardId), lotteryType, awardDetailsConfigCaches);
      }

      return awardDetailsConfigCaches;
    });
  }

  @Override
  public void removeAwardDetailsConfigCache(Long awardId, String lotteryType) {

    if (Objects.isNull(awardId)) {
      return;
    }
    redisService.hashDelete(
        getGameLuckyBoxAwardConfigDetailsKey(awardId), lotteryType);
  }

  @Override
  public GameLuckyBoxFortuneValueConfigCache getFortuneValueConfigCache(
      VoidCallable<GameLuckyBoxFortuneValueConfigCache> orElsePut) {
    return Optional
        .ofNullable(redisService.getStringToObject(getFortuneValueConfigKey(),
            GameLuckyBoxFortuneValueConfigCache.class))
        .orElseGet(() -> {
          GameLuckyBoxFortuneValueConfigCache fortuneValueConfigCache = orElsePut.apply();
          if (Objects.nonNull(fortuneValueConfigCache)) {
            redisService.delete(getFortuneValueConfigKey());
            redisService.setString(getFortuneValueConfigKey(), fortuneValueConfigCache);
          }
          return fortuneValueConfigCache;
        });
  }

  @Override
  public void removeFortuneValueConfigCache() {
    redisService.delete(getFortuneValueConfigKey());
  }

  @Override
  public List<GameLuckyBoxInventoryCache> getInventoryTemplate(Long standardId, Integer sort) {
    return redisService.hashGet(getInventoryTemplateKey(standardId),
        String.valueOf(sort), new TypeReference<>() {
        });
  }

  @Override
  public void cacheInventoryTemplate(Long standardId, Integer sort,
      List<GameLuckyBoxInventoryCache> gameLuckyBoxInventoryCaches) {
    String key = getInventoryTemplateKey(standardId);
    redisService.hashDelete(key, String.valueOf(sort));
    redisService.hashPut(key, String.valueOf(sort), gameLuckyBoxInventoryCaches);
  }

  @Override
  public void removeInventoryTemplate(Long standardId, Integer sort) {
    String key = getInventoryTemplateKey(standardId);
    redisService.hashDelete(key, String.valueOf(sort));
  }

  @Override
  public List<GameLuckyBoxInventoryCache> initRemainInventory(Long standardId, Integer sort,
      List<GameLuckyBoxInventoryCache> caches) {
    if (CollectionUtils.isEmpty(caches)) {
      log.error("【lucky box游戏】没有礼物，签数数据，初始化失败");
      return Lists.newArrayList();
    }

    try {

      if (redisService.lock(getRemainingInventoryLockKey(standardId, sort), 10)) {

        caches.forEach(cache -> {

          String key = getRemainingInventoryKey(standardId, sort, cache.getGiftConfigId());
          redisService.delete(key);
          redisService.setString(key, cache.getQuantity());
        });
        return caches;
      }
    } finally {
      redisService.unlock(getRemainingInventoryLockKey(standardId, sort));
    }

    return Lists.newArrayList();
  }

  @Override
  public Map<Long, Integer> mapRemainInventory(Long standardId, Integer sort,
      Set<Long> giftConfigIds) {
    Map<Long, Integer> dataMap = Maps.newHashMap();
    giftConfigIds.forEach(giftConfigId -> {
      String inventoryKey = getRemainingInventoryKey(standardId, sort, giftConfigId);

      Integer inventory = Optional.ofNullable(
          redisService.getStringToInteger(inventoryKey)).orElse(0);

      dataMap.put(giftConfigId, inventory >= 0 ? inventory : 0);
    });

    return dataMap;
  }

  @Override
  public void removeRemainingInventory(Long standardId, Integer sort) {
    if (Objects.isNull(standardId)) {
      return;
    }
    List<GameLuckyBoxInventoryCache> cache = getInventoryTemplate(standardId, sort);
    if (CollectionUtils.isEmpty(cache)) {
      return;
    }
    // 清理模版
    removeInventoryTemplate(standardId, sort);

    // 清理实时库存
    cache.forEach(inventoryCache -> redisService.delete(
        getRemainingInventoryKey(standardId, sort, inventoryCache.getGiftConfigId())));

  }

  @Override
  public Long decrInventoryQuantity(Long standardId, Integer sort, Long giftConfigId) {
    return redisService.decrement(
        getRemainingInventoryKey(standardId, sort, giftConfigId), 1);
  }

  private String getGameLuckyBoxTimeConfigKey(Long standardId) {
    return GameNewLuckyBoxKeys.NEW_TIME_CONFIG.getKey(standardId);
  }

  private String getGameLuckyBoxGiftConfigDetailsKey(Long standardId) {
    return GameNewLuckyBoxKeys.NEW_GIFT_CONFIG.getKey(standardId);
  }

  private String getGameLuckyBoxAwardConfigDetailsKey(Long awardId) {
    return GameNewLuckyBoxKeys.NEW_AWARD_CONFIG.getKey(awardId);
  }

  private String getFortuneValueConfigKey() {
    return GameNewLuckyBoxKeys.NEW_FORTUNE_VALUE_CONFIG.getKey();
  }

  private String getInventoryTemplateKey(Long standardId) {
    return GameNewLuckyBoxKeys.NEW_INVENTORY_TEMPLATE.getKey(standardId);
  }

  private String getRemainingInventoryLockKey(Long standardId, Integer sort) {
    return GameNewLuckyBoxKeys.NEW_REMAINING_INVENTORY_LOCK.getKey(standardId, sort);
  }

  private String getRemainingInventoryKey(Long standardId, Integer sort, Long giftConfigId) {
    return GameNewLuckyBoxKeys.NEW_REMAINING_INVENTORY.getKey(standardId, sort, giftConfigId);
  }
}
