package com.red.circle.other.app.scheduler;

import com.alibaba.fastjson.JSON;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.other.app.command.material.PropsPurchasingCmdExe;
import com.red.circle.other.domain.gateway.props.PropsStoreGateway;
import com.red.circle.other.infra.common.activity.PropsActivitySendCommon;
import com.red.circle.other.infra.common.activity.send.SendRewardGroup;
import com.red.circle.other.infra.database.mongo.entity.activity.RoomFanVotesActivityCount;
import com.red.circle.other.infra.database.mongo.service.activity.RoomFanVotesActivityCountService;
import com.red.circle.other.infra.database.rds.dao.props.RunningWaterUserPropsDAO;
import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRuleConfig;
import com.red.circle.other.infra.database.rds.entity.props.PropsBackpack;
import com.red.circle.other.infra.database.rds.entity.props.PropsNobleVipAbility;
import com.red.circle.other.infra.database.rds.entity.props.RunningWaterUserProps;
import com.red.circle.other.infra.database.rds.enums.CacheKeysEnum;
import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRuleConfigService;
import com.red.circle.other.infra.database.rds.service.family.FamilyDailyTaskTriggerRecordService;
import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService;
import com.red.circle.other.infra.database.rds.service.props.RunningWaterUserPropsService;
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.model.dto.material.props.PropsStoreCommodity;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.google.common.collect.Lists;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.red.circle.tool.core.date.LocalDateTimeUtils;
import com.red.circle.tool.core.tuple.ImmutableKeyValuePair;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import static com.red.circle.common.business.core.enums.SysOriginPlatformEnum.LIKEI;

/**
 * 每日任务数据.
 *
 * @author pengshigang on 2022/8/19
 */
@Slf4j
@Component
@AllArgsConstructor
public class DailyTask {

  private final PropsActivitySendCommon sendPropsManager;
  private final RedisService redisService;
  private final PropsActivityRuleConfigService propsActivityRuleConfigService;
  private final RoomFanVotesActivityCountService roomFanVotesActivityCountService;
  private final FamilyDailyTaskTriggerRecordService familyDailyTaskTriggerRecordService;
  private final PropsStoreGateway propsStoreGateway;
  private final RunningWaterUserPropsService runningWaterUserPropsService;
  private final RunningWaterUserPropsDAO runningWaterUserPropsDAO;
  private final PropsBackpackService propsBackpackService;
  private final WalletGoldClient walletGoldClient;
  private final PropsPurchasingCmdExe  propsPurchasingCmdExe;

  /**
   * 每日处理数据.
   */
  @Scheduled(cron = "0 0 0 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "DAILY_PROCESS_TASK", expireSecond = 10000)
  public void dailyTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec daily_process_task start");
    // 删除家族每日任务数据
    familyDailyTaskTriggerRecordService.delete().execute();
    log.info("exec daily_process_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日19:31处理 [印度，孟加拉，巴基斯坦] 区域粉丝人气票活动发奖.
   */
  @Scheduled(cron = "0 31 19 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_IN_TASK", expireSecond = 10000)
  public void roomFanVotesActivityInTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_in_task start");
    log.warn("Start - 每日19:31处理 [印度，孟加拉，巴基斯坦] 区域粉丝人气票活动发奖");
    // 重置倒计时
    cacheCountDown(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_IN_COUNT_DOWN);

    SysOriginPlatformEnum.getVoiceSystems().forEach(sysOrigin -> {
      sendFanVotesReward(sysOrigin.name(), "IN");
      sendFanVotesReward(sysOrigin.name(), "PK");
      sendFanVotesReward(sysOrigin.name(), "BD");
    });

    log.warn("End - 每日19:31处理 [印度，孟加拉，巴基斯坦] 区域粉丝人气票活动发奖");
    log.info("exec room_fan_votes_activity_in_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日18:30[印度，孟加拉，巴基斯坦] 重置倒计时，重置榜单.
   */
  @Scheduled(cron = "0 30 18 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_IN_START_TASK", expireSecond = 10000)
  public void roomFanVotesActivityInStartTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_in_start_task start");
    // 重置倒计时 60分钟
    long countDown = Duration.between(ZonedDateTimeUtils.nowAsiaRiyadh(),
        ZonedDateTimeUtils.nowAsiaRiyadh().plusHours(1)).toMillis() - 1000;
    redisService.setString(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_IN_COUNT_DOWN.name(),
        countDown, countDown, TimeUnit.MILLISECONDS);

    List<String> codes = List.of("IN", "PK", "BD");

    // 将昨天数据修改历史
    roomFanVotesActivityCountService.upHistory(codes);

    // 删除昨天之前的数据
    roomFanVotesActivityCountService.del(codes);
    log.info("exec room_fan_votes_activity_in_start_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日16:31处理 [印尼] 区域粉丝人气票活动发奖.
   */
  @Scheduled(cron = "0 31 16 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_ID_TASK", expireSecond = 10000)
  public void roomFanVotesActivityIdTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_id_task start");
    log.warn("Start - 每日16:31处理 [印尼] 区域粉丝人气票活动发奖");

    // 重置倒计时
    cacheCountDown(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_ID_COUNT_DOWN);
    SysOriginPlatformEnum.getVoiceSystems()
        .forEach(sysOrigin -> sendFanVotesReward(sysOrigin.name(), "ID"));

    log.warn("End - 每日16:31处理 [印尼] 区域粉丝人气票活动发奖");
    log.info("exec room_fan_votes_activity_id_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日15:30[印尼] 重置倒计时，重置榜单.
   */
  @Scheduled(cron = "0 30 15 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_ID_START_TASK", expireSecond = 10000)
  public void roomFanVotesActivityIdStartTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_id_start_task start");
    // 重置倒计时 60分钟
    long countDown = Duration.between(ZonedDateTimeUtils.nowAsiaRiyadh(),
        ZonedDateTimeUtils.nowAsiaRiyadh().plusHours(1)).toMillis() - 1000;
    redisService.setString(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_ID_COUNT_DOWN.name(),
        countDown, countDown, TimeUnit.MILLISECONDS);

    List<String> codes = List.of("ID");

    // 将昨天数据修改历史
    roomFanVotesActivityCountService.upHistory(codes);

    // 删除昨天之前的数据
    roomFanVotesActivityCountService.del(codes);
    log.info("exec room_fan_votes_activity_id_start_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日22:31处理 [阿拉伯] 区域粉丝人气票活动发奖.
   */
  @Scheduled(cron = "0 31 22 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_AR_TASK", expireSecond = 10000)
  public void roomFanVotesActivityArTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_ar_task start");
    log.warn("Start - 每日22:31处理 [阿拉伯] 区域粉丝人气票活动发奖");

    // 重置倒计时
    cacheCountDown(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_AR_COUNT_DOWN);

    SysOriginPlatformEnum.getVoiceSystems()
        .forEach(sysOrigin -> sendFanVotesReward(sysOrigin.name(), "AR"));

    log.warn("End - 每日22:31处理 [阿拉伯] 区域粉丝人气票活动发奖");
    log.info("exec room_fan_votes_activity_ar_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每日21:30[阿拉伯] 重置倒计时，重置榜单.
   */
  @Scheduled(cron = "0 30 21 1/1 * ?", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "ROOM_FAN_VOTES_ACTIVITY_AR_START_TASK", expireSecond = 10000)
  public void roomFanVotesActivityArStartTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec room_fan_votes_activity_ar_start_task start");
    // 重置倒计时 60分钟
    long countDown = Duration.between(ZonedDateTimeUtils.nowAsiaRiyadh(),
        ZonedDateTimeUtils.nowAsiaRiyadh().plusHours(1)).toMillis() - 1000;
    redisService.setString(CacheKeysEnum.ROOM_FAN_VOTES_ACTIVITY_AR_COUNT_DOWN.name(),
        countDown, countDown, TimeUnit.MILLISECONDS);

    List<String> codes = List.of("AR");

    // 将昨天数据修改历史
    roomFanVotesActivityCountService.upHistory(codes);

    // 删除昨天之前的数据
    roomFanVotesActivityCountService.del(codes);
    log.info("exec room_fan_votes_activity_ar_start_task end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 贵族每日金币奖励发放 - 沙特时间每日0点执行.
   */
  @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Riyadh")
  @TaskCacheLock(key = "NOBLE_DAILY_GOLD_REWARD_TASK", expireSecond = 72000 )
  public void nobleDailyGoldRewardTask() {
    long startTime = System.currentTimeMillis();
    log.info("exec noble_daily_gold_reward_task start");
    log.warn("Start - 贵族每日金币奖励发放");

      ImmutableKeyValuePair<String, String> valuePair = ImmutableKeyValuePair.of("PURCHASE_NOBLE_VIP_GIVEAWAY", "Give Noble");
      try {
        List<PropsStoreCommodity> propsStoreCommodities = propsStoreGateway.listReleaseStoreCommodity(LIKEI, PropsCommodityType.NOBLE_VIP);
        
        // 按照VIP等级从大到小排序
          List<PropsStoreCommodity> commodityList = propsStoreCommodities.stream()
                  .filter(e -> Objects.nonNull(e.getNobleVipAbility()))
                  .sorted(Comparator.comparing(e -> {
                      PropsStoreCommodity commodity = (PropsStoreCommodity) e;
                      return commodity.getNobleVipAbility().getVipLevel();
                  }).reversed())
                  .toList();

          List<Long> processedUsers = Lists.newArrayList(); // 记录已处理的用户，避免重复发放

          commodityList.forEach(propsStoreCommodity -> {
              Long propsId = propsStoreCommodity.getPropsResources().getId();
              List<PropsBackpack> propsBackpackList = propsBackpackService.listUsersByNobleVipAndPropsId(PropsCommodityType.NOBLE_VIP, propsId);
            log.info("VIP用户: {}", JSON.toJSONString(propsBackpackList));

            // 提取用户ID列表
            List<Long> allUsers = propsBackpackList.stream()
                .map(PropsBackpack::getUserId)
                .distinct()
                .collect(Collectors.toList());

            // 查询用户是否处于可赠送金币区间
            List<Long> eligibleUsers = getEligibleGiftUsers(allUsers, propsId);

            // 给每个用户发送奖励，排除已处理的用户
           for (Long userId : eligibleUsers) {
               if (!processedUsers.contains(userId)) {
                   ResultResponse<WalletReceiptResDTO> res = walletGoldClient.changeBalance(GoldReceiptCmd.builder()
                           .appIncome()
                           .userId(userId)
                           .eventId(propsStoreCommodity.getId())
                           .sysOrigin(LIKEI)
                           .origin(valuePair.getKey())
                           .originDescribe(valuePair.getValue())
                           .amount(propsStoreCommodity.getNobleVipAbility().getLoginRewardsAmount())
                           .build()
                   );

                   if (res == null || !res.checkSuccess()) {
                       log.error("贵族每日金币奖励发放失败 - userId: {}, propsId: {}, amount: {}, error: {}",
                               userId, propsStoreCommodity.getId(),
                               propsStoreCommodity.getNobleVipAbility().getLoginRewardsAmount(),
                               res != null ? res.getErrorMsg() : "响应为空");
                   } else {
                       log.info("贵族每日金币奖励发放成功 - userId: {}, amount: {}", userId,
                               propsStoreCommodity.getNobleVipAbility().getLoginRewardsAmount());
                   }

                   processedUsers.add(userId); // 标记为已处理
               } else {
                 log.warn("已处理过不再重复处理： {}", userId);
               }
           }
        });

      log.warn("贵族每日金币奖励发放完成");
      
    } catch (Exception ex) {
      log.error("贵族每日金币奖励发放异常: {}", ex.getMessage(), ex);
    }
    
    log.warn("End - 贵族每日金币奖励发放");
    log.info("exec noble_daily_gold_reward_task end with {}", System.currentTimeMillis() - startTime);
  }

  /**
   * 查询用户是否处于可赠送金币区间
   * 规则：
   * 1. 查询NOBLE_VIP的origin为PURCHASING_GOLD_PROPS类型的，按照创建时间加天数计算
   * 2. 如果当前时间在范围内则为有效赠送用户
   * 3. 如果没有PURCHASING_GOLD_PROPS类型，则其余不赠送
   */
  private List<Long> getEligibleGiftUsers(List<Long> allUsers, Long propsId) {
    if (CollectionUtils.isEmpty(allUsers)) {
      return Lists.newArrayList();
    }

    List<Long> eligibleUsers = Lists.newArrayList();
    LocalDateTime now = LocalDateTime.now();

    for (Long userId : allUsers) {
      if (isUserEligibleForGift(userId, now, propsId)) {
        eligibleUsers.add(userId);
      }
    }

    log.info("所有贵族用户数: {}, 符合赠送条件的用户数: {}", allUsers.size(), eligibleUsers.size());
    return eligibleUsers;
  }

  /**
   * 判断单个用户是否符合赠送条件
   */
  private boolean isUserEligibleForGift(Long userId, LocalDateTime currentTime, Long propsId) {
    try {
      // 查询用户NOBLE_VIP类型的所有流水记录
      List<RunningWaterUserProps> allRecords = runningWaterUserPropsService.query()
          .eq(RunningWaterUserProps::getUserId, userId)
          .eq(RunningWaterUserProps::getPropsId, propsId)
          .eq(RunningWaterUserProps::getType, "NOBLE_VIP")
          .orderByAsc(RunningWaterUserProps::getCreateTime)
          .list();

      if (CollectionUtils.isEmpty(allRecords)) {
        log.debug("用户 {} 没有NOBLE_VIP类型的流水记录，不符合赠送条件", userId);
        return false;
      }

      // 分离购买记录和扣除记录
      List<RunningWaterUserProps> purchasingRecords = allRecords.stream()
          .filter(DailyTask::validType)
          .collect(Collectors.toList());

      // 检查非PURCHASING_GOLD_PROPS类型的扣除天数记录
      List<RunningWaterUserProps> deductionRecords = allRecords.stream()
          .filter(record -> !validType(record) && record.getDays() != null && record.getDays() < 0)
          .collect(Collectors.toList());

      if (CollectionUtils.isEmpty(purchasingRecords)) {
        log.debug("用户 {} 没有PURCHASING_GOLD_PROPS类型的流水记录，不符合赠送条件", userId);
        return false;
      }

      // 计算累计有效天数
      LocalDateTime earliestValidTime = calculateEffectiveValidTime(purchasingRecords, deductionRecords, currentTime);
      
      if (earliestValidTime == null) {
        log.debug("用户 {} 没有有效的购买记录或所有记录都已过期，不符合赠送条件", userId);
        return false;
      }

      // 判断当前时间是否在有效期内
      boolean isValid = currentTime.isBefore(earliestValidTime) || currentTime.isEqual(earliestValidTime);
      
      log.debug("用户 {} 赠送条件检查结果: {}, 当前时间: {}, 有效期截止时间: {}", 
          userId, isValid ? "符合" : "不符合", currentTime, earliestValidTime);
      
      return isValid;

    } catch (Exception e) {
      log.error("检查用户 {} 赠送资格时发生异常: {}", userId, e.getMessage(), e);
      return false;
    }
  }

  private static boolean validType(RunningWaterUserProps record) {
    return Arrays.asList("COUPON_EXCHANGE", "PURCHASING_GOLD_PROPS").contains(record.getOrigin());
  }

  /**
   * 计算用户的有效期截止时间
   * 根据购买记录和扣除记录，结合时间顺序累计计算有效期
   */
  private LocalDateTime calculateEffectiveValidTime(List<RunningWaterUserProps> purchasingRecords, 
                                                   List<RunningWaterUserProps> deductionRecords, 
                                                   LocalDateTime currentTime) {

    // 所有记录按时间排序
    purchasingRecords.sort(Comparator.comparing(RunningWaterUserProps::getCreateTime));
    deductionRecords.sort(Comparator.comparing(RunningWaterUserProps::getCreateTime));

    LocalDateTime currentValidEndTime = null;
    boolean hasValidRecord = false;

    // 先处理所有购买记录
    for (RunningWaterUserProps record : purchasingRecords) {
      LocalDateTime recordCreateTime = record.getCreateTime().toLocalDateTime();
      Integer days = record.getDays();

      if (days != null && days > 0) {
        if (currentValidEndTime == null || recordCreateTime.isAfter(currentValidEndTime)) {
          // 如果当前没有有效期或购买时间在当前有效期之后，从购买时间开始计算
          currentValidEndTime = recordCreateTime.plusDays(days);
        } else {
          // 如果购买时间在当前有效期内，在当前有效期基础上延长
          currentValidEndTime = currentValidEndTime.plusDays(days);
        }
        hasValidRecord = true;
        log.debug("用户购买记录增加天数: {}, 购买时间: {}, 累计有效期至: {}", days, recordCreateTime, currentValidEndTime);
      }
    }

    // 再处理所有扣除记录
    for (RunningWaterUserProps record : deductionRecords) {
      LocalDateTime recordCreateTime = record.getCreateTime().toLocalDateTime();
      Integer days = record.getDays();

      if (days == null || days >= 0) {
        log.warn("扣除天数无效：{}", days);
        continue;
      }

      if (currentValidEndTime == null) {
        log.warn("无有效天数：{}", currentValidEndTime);
        continue;
      }

      if (recordCreateTime.isBefore(currentValidEndTime)) {
        // 只有在有有效期且扣除时间在有效期内才能扣除
        LocalDateTime originalValidEndTime = currentValidEndTime;
        currentValidEndTime = currentValidEndTime.plusDays(days); // days是负数，相当于减少天数
        log.debug("用户扣除记录减少天数: {}, 扣除时间: {}, 原有效期: {}, 调整后有效期: {}",
                 days, recordCreateTime, originalValidEndTime, currentValidEndTime);

        // 如果扣除后有效期已经过了当前时间或扣除时间之前，则无效
        if (currentValidEndTime.isBefore(currentTime) || currentValidEndTime.isBefore(recordCreateTime)) {
          currentValidEndTime = null;
          hasValidRecord = false;
          log.debug("扣除后有效期已过期或无效，重置有效期");
        }
      }
    }

    return hasValidRecord ? currentValidEndTime : null;
  }

  private void sendFanVotesReward(String code, String sysOrigin) {

    List<RoomFanVotesActivityCount> votesCounts = roomFanVotesActivityCountService
        .listCurrent(code, sysOrigin, 10);
    if (CollectionUtils.isEmpty(votesCounts)) {
      log.warn("[execute] {} 区域粉丝人气票活动发奖,没有房间获奖", code);
      return;
    }

    List<PropsActivityRuleConfig> resources = listRuleConfigs(sysOrigin);
    if (CollectionUtils.isEmpty(resources) || resources.size() < 3) {
      log.warn("[execute] {} 区域粉丝人气票活动发奖,资源数据不对", code);
      return;
    }

    log.warn("[execute] {} 区域粉丝人气票活动发奖,获奖数据:{}", code, votesCounts);

    try {
      for (int index = 0; index < votesCounts.size(); index++) {

        if (index > 2) {
          break;
        }

        RoomFanVotesActivityCount count = votesCounts.get(index);

        if (Objects.equals(count.getAlreadySent(), Boolean.TRUE)) {
          log.warn("[execute] {} 区域粉丝人气票活动发奖,已发过奖,无需重复再发:{}", code, count);
          continue;
        }

        PropsActivityRuleConfig resource = resources.get(index);
        // 发送奖励
        sendPropsManager.sendActivityGroup(SendRewardGroup.builder()
            .trackId(resource.getId())
            .sysOrigin(SysOriginPlatformEnum.valueOf(sysOrigin))
            .acceptUserId(count.getUserId())
            .resourceGroupId(resource.getResourceGroupId())
            .origin(SendPropsOrigin.ROOM_FAN_VOTES_ACTIVITY)
            .build());
      }
    } catch (Exception ex) {
      log.warn("[execute] {} 区域粉丝人气票活动发奖: {}", code, ex.getMessage());
    }

    // 修改区域为已经领取
    roomFanVotesActivityCountService.upAlreadySent(code);

  }

  private List<PropsActivityRuleConfig> listRuleConfigs(String sysOrigin) {

    return propsActivityRuleConfigService.listPlatformByActivityType(
        sysOrigin,
        PropsActivityTypeEnum.ROOM_FAN_VOTES_ACTIVITY);
  }

  /**
   * 缓存区域粉丝人气票活动倒计时.
   */
  private void cacheCountDown(CacheKeysEnum keysEnum) {

    long countDown = Duration.between(ZonedDateTimeUtils.nowAsiaRiyadh(),
        ZonedDateTimeUtils.nowAsiaRiyadh().plusDays(1)).toMillis() - 3660000;
    redisService.setString(keysEnum.name(), countDown, countDown, TimeUnit.MILLISECONDS);

  }
}
