package com.red.circle.other.app.command.activity;

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.command.user.InviteCodeService;
import com.red.circle.other.app.dto.cmd.activity.BindInviteCodeCmd;
import com.red.circle.other.app.service.SpinsUserTaskProgressService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.rds.entity.user.user.UserInviteUser;
import com.red.circle.other.infra.database.rds.service.user.user.UserInviteUserService;
import com.red.circle.other.infra.enums.user.InviteTypeEnum;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 绑定邀请码执行器
 *
 * @author system
 * @date 2025-01-21
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class BindInviteCodeExe {

    private final UserProfileGateway userProfileGateway;
    private final UserInviteUserService userInviteUserService;
    private final InviteCodeService inviteCodeService;
    private final RedisService redisService;
    private final SpinsUserTaskProgressService spinsUserTaskProgressService;
    private final RegisterDeviceGateway registerDeviceGateway;
    private final TaskMqMessage taskMqMessage;

    /**
     * 执行绑定邀请码
     *
     * @param cmd 绑定命令
     */
    @Transactional(rollbackFor = Exception.class)
    public void execute(BindInviteCodeCmd cmd) {
        Long userId = cmd.getReqUserId();
        String inviteCode = cmd.getInviteCode().trim();
        String inviteType = cmd.getInviteType();
        String sysOrigin = cmd.getReqSysOrigin().getOrigin();

        log.info("开始绑定邀请码, userId={}, inviteCode={}, inviteType={}", userId, inviteCode, inviteType);

        // 0. 每个设备只允许绑定一次
        String deviceId = getDeviceId(userId);
        String deviceBindKey = "invite:device:bind:" + deviceId;
        
        // 检查该设备是否已经绑定过
        String boundUserId = redisService.getString(deviceBindKey);
        if (StringUtils.isNotBlank(boundUserId)) {
            log.warn("该设备已绑定过邀请码，不允许重复绑定, deviceId={}, boundUserId={}, currentUserId={}", 
                    deviceId, boundUserId, userId);
            throw new RuntimeException("This device has already bound an invitation code and cannot bind again");
        }

      // 1. 校验inviteType是否合法
        if (!InviteTypeEnum.LOTTERY.getCode().equals(inviteType) && 
            !InviteTypeEnum.REGISTER.getCode().equals(inviteType)) {
            throw new RuntimeException("Invalid invitation type, must be LOTTERY or REGISTER");
        }

        // 3. 校验用户是否存在
        UserProfile userProfile = userProfileGateway.getByUserId(userId);
        if (userProfile == null) {
          throw new RuntimeException("The user does not exist.");
        }

        // 校验用户创建时间是否超过7天 (11月2号0点后生效)
        Timestamp createTime = userProfile.getCreateTime();
        if (createTime == null) {
            throw new RuntimeException("The user createTime does not exist.");
        }

        long threeDaysInMillis = 7 * 24 * 60 * 60 * 1000L; // 7天的毫秒数
        long timeDiff = TimestampUtils.now().getTime() - createTime.getTime();

        if (timeDiff > threeDaysInMillis) {
          throw new RuntimeException("User registration time has exceeded 3 days and cannot bind invitation code");
        }

        // 4. 校验是否已绑定过邀请人（userId=邀请人, inviteUserId=被邀请人）
        // 当前用户作为被邀请人，查询 inviteUserId=当前用户
        List<UserInviteUser> existingInvite = userInviteUserService.query()
                .eq(UserInviteUser::getInviteUserId, userId) // 当前用户作为被邀请人
                .list();
        if (existingInvite != null && !existingInvite.isEmpty()) {
            throw new RuntimeException("The inviter has been bound before and cannot be bound again");
        }

        // 5. 根据邀请码查找邀请人
        UserProfile inviterProfile = inviteCodeService.getUserProfileByInviteCode(inviteCode);
        if (inviterProfile == null) {
            throw new RuntimeException("The invitation code is invalid and the corresponding user was not found");
        }

        // 6. 校验不能邀请自己
        if (Objects.equals(userId, inviterProfile.getId())) {
            throw new RuntimeException("You cannot bind your own invitation code");
        }

        // 再次校验：如果当前用户已经绑定了其他邀请人，则不能再绑定
        // 这里是双重校验，确保数据一致性
        List<UserInviteUser> doubleCheck = userInviteUserService.query()
                .eq(UserInviteUser::getInviteUserId, userId)
                .list();
        if (doubleCheck != null && !doubleCheck.isEmpty()) {
            Long existingInviterId = doubleCheck.get(0).getUserId();
            log.warn("用户已绑定邀请人，不能重复绑定, userId={}, existingInviterId={}, newInviterId={}", 
                    userId, existingInviterId, inviterProfile.getId());
            throw new RuntimeException("You have already bound an inviter and cannot bind again");
        }

        // 校验双向绑定：如果邀请人已经被当前用户邀请过，则不能再绑定
        // 即：如果 inviterProfile 已经是当前用户的被邀请人，则不能让 inviterProfile 成为当前用户的邀请人
        // 查询：userId=当前用户, inviteUserId=邀请人（检查当前用户是否已经邀请过这个邀请人）
        List<UserInviteUser> reverseInvite = userInviteUserService.query()
            .eq(UserInviteUser::getUserId, userId) // 当前用户作为邀请人
            .eq(UserInviteUser::getInviteUserId, inviterProfile.getId()) // 邀请人作为被邀请人
            .list();
        if (reverseInvite != null && !reverseInvite.isEmpty()) {
          log.warn("不能双向绑定，该用户已被当前用户邀请过, userId={}, inviterUserId={}",
              userId, inviterProfile.getId());
          throw new RuntimeException("Cannot bind bidirectionally, this user has already been invited by you");
        }

        // 7. 保存邀请关系（userId=邀请人, inviteUserId=被邀请人）
        UserInviteUser inviteUser = new UserInviteUser();
        inviteUser.setId(IdWorkerUtils.getId());
        inviteUser.setSysOrigin(sysOrigin);
        inviteUser.setUserId(inviterProfile.getId()); // 邀请人
        inviteUser.setInviteUserId(userId); // 被邀请人（当前用户）
        inviteUser.setInviteType(inviteType); // 使用传入的inviteType
        inviteUser.setCreateTime(TimestampUtils.now());
        inviteUser.setUpdateTime(TimestampUtils.now());

        userInviteUserService.save(inviteUser);

        // 记录该设备已绑定（永久有效）
        redisService.setString(deviceBindKey, userId.toString());

        log.info("绑定邀请码成功, userId={}, inviterUserId={}, inviteCode={}, inviteType={}, deviceId={}", 
                userId, inviterProfile.getId(), inviteCode, inviteType, deviceId);

        userProfileGateway.removeCacheAll(Collections.singleton(userId));

        // 增加用户每日邀请用户数量
//        int inviteCount = incrementDailyInviteCount(inviterProfile.getId());

        // 处理送礁1个礼物任务
        /*if (inviteCount >= 1) {
          spinsUserTaskProgressService.updateTaskProgress(
              new SpinsTaskProgressUpdateCmd()
                  .setUserId(inviterProfile.getId())
                  .setTaskCode("SPINS_INVITE_USER_1")
                  .setProgressValue(inviteCount)
          );
        }

        // 处理送出3个礼物任务
        if (inviteCount >= 3) {
          spinsUserTaskProgressService.updateTaskProgress(
              new SpinsTaskProgressUpdateCmd()
                  .setUserId(inviterProfile.getId())
                  .setTaskCode("SPINS_INVITE_USER_3")
                  .setProgressValue(inviteCount)
          );
        }*/

        // 关注一个用户任务
        taskMqMessage.sendTask(TaskApprovalEvent.builder()
            .taskId(11)
            .userId(cmd.requiredReqUserId())
            .build());
    }

  /**
   * 获取设备指纹（唯一标识）
   * @param userId 用户ID
   * @return 设备指纹字符串或用户ID
   */
  private String getDeviceId(Long userId) {
    try {
      String fingerprint = registerDeviceGateway.getDeviceFingerprintByUserId(userId);
      
      if (StringUtils.isNotBlank(fingerprint)) {
        log.info("获取到设备指纹, userId={}, fingerprint={}", userId, fingerprint);
        return fingerprint;
      }
    } catch (Exception e) {
      log.warn("获取设备指纹失败, userId={}, error={}", userId, e.getMessage());
    }
    
    // 如果没有设备指纹，使用userId
    log.info("未获取到设备指纹，使用userId作为设备标识, userId={}", userId);
    return "USER_" + userId;
  }

  /**
   * 增加用户每日邀请用户数量
   * @param userId 用户ID
   * @return 当前邀请用户数量
   */
  private int incrementDailyInviteCount(Long userId) {
    String redisKey = "spins:daily:inviteuser:count:" + userId;
    String countStr = redisService.getString(redisKey);

    if (StringUtils.isBlank(countStr)) {
      // 计算到凌晨的过期时间（秒）
      long expireSeconds = getSecondsUntilMidnight();
      redisService.setString(redisKey, "1", expireSeconds, TimeUnit.SECONDS);
      return 1;
    } else {
      Long newCount = redisService.increment(redisKey, 1);
      return newCount.intValue();
    }
  }


  /**
   * 获取当前时间到凌晨的秒数
   * @return 秒数
   */
  private long getSecondsUntilMidnight() {
    ZonedDateTime now = ZonedDateTimeAsiaRiyadhUtils.now();
    ZonedDateTime midnight = now.toLocalDate().plusDays(1).atStartOfDay(now.getZone());
    return java.time.Duration.between(now, midnight).getSeconds();
  }


}
