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

import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.model.user.ability.RegionConfig;
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
import com.red.circle.other.domain.gateway.PropCouponGateway;
import com.red.circle.other.domain.propcoupon.PropCoupon;
import com.red.circle.other.domain.propcoupon.PropCouponSource;
import com.red.circle.other.domain.propcoupon.PropCouponStatus;
import com.red.circle.other.domain.propcoupon.PropCouponType;
import com.red.circle.other.inner.asserts.OtherErrorCode;
import com.red.circle.other.inner.endpoint.material.props.PropsSourceClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 后台赠送道具券执行器
 *
 * @author system
 * @date 2025-11-05
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class PropCouponGrantCmdExe {

    private final PropCouponGateway propCouponGateway;
    private final OfficialNoticeClient officialNoticeClient;
    private final PropsSourceClient  propsSourceClient;
    private final UserProfileGateway userProfileGateway;
    private final UserRegionGateway userRegionGateway;

    /**
     * 执行后台赠送道具券
     */
    @Transactional(rollbackFor = Exception.class)
    public void execute(PropCouponGrantCmd cmd) {
        // 1. 校验参数
        validateParams(cmd);

        // 2. 校验用户是否存在

        Set<Long> userIds = cmd.getUserIds().stream().map(Long::parseLong).collect(Collectors.toSet());
        Map<Long, UserProfile> userMap = validateUsers(userIds);

        PropsResourcesDTO propsResourcesDTO = ResponseAssert.requiredSuccess(
            propsSourceClient.getById(Long.parseLong(cmd.getPropId())));
        ResponseAssert.notNull(OtherErrorCode.PROP_COUPON_PROP_NOT_FOUND, propsResourcesDTO);

        PropCouponType couponType = PropCouponType.getByName(propsResourcesDTO.getType());
        ResponseAssert.notNull(OtherErrorCode.PROP_COUPON_TYPE_NOT_SUPPORT, couponType);

        cmd.setPropIcon(propsResourcesDTO.getCover());
        cmd.setPropName(propsResourcesDTO.getName());
        cmd.setCouponType(couponType.getCode());

        List<PropCoupon> propCoupons = generateCoupons(cmd, couponType, userMap.keySet());

        // 5. 批量插入券记录
        propCouponGateway.batchSave(propCoupons);

        // 6. 发送系统消息通知用户
        sendNotifications(propCoupons, userMap, couponType);

        log.info("后台赠送道具券成功, couponType:{}, propId:{}, userCount:{}, totalCoupons:{}",
                couponType.getDesc(), cmd.getPropId(), userMap.size(), propCoupons.size());
    }

    /**
     * 校验参数
     */
    private void validateParams(PropCouponGrantCmd cmd) {
        // 校验用户数量不能超过限制
        ResponseAssert.isFalse(OtherErrorCode.PROP_COUPON_BATCH_USER_LIMIT,
                cmd.getUserIds() == null || cmd.getUserIds().size() > 100);

        // 校验有效天数不能超过限制
        ResponseAssert.isFalse(OtherErrorCode.PROP_COUPON_VALID_DAYS_LIMIT,
                cmd.getValidDays() <= 0 || cmd.getValidDays() > 365);
    }

    /**
     * 校验用户是否存在
     */
    private Map<Long, UserProfile> validateUsers(Set<Long> userIds) {
        Map<Long, UserProfile> userMap = userProfileGateway.mapByUserIds(userIds);

        if (userMap.isEmpty()) {
            Map<Long, UserProfile> map = new HashMap<>();
            userIds.forEach(userId -> {
                List<UserProfile> userProfiles = userProfileGateway.getByAccount(String.valueOf(userId));
                if (!userProfiles.isEmpty()) {
                    UserProfile userProfile = userProfiles.get(0);
                    map.put(userProfile.getId(), userProfile);
                }
            });
            return map;
        }

        // 校验所有用户都存在
        ResponseAssert.isTrue(OtherErrorCode.PROP_COUPON_RECEIVER_NOT_FOUND,
                userMap.size() == userIds.size());

        return userMap;
    }

    /**
     * 批量生成券
     */
    private List<PropCoupon> generateCoupons(PropCouponGrantCmd cmd, PropCouponType couponType, Set<Long> userIds) {
        List<PropCoupon> propCoupons = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expireTime = now.plusDays(cmd.getValidDays());

        // 转换贵族等级名称为VIP等级
        String displayPropName = convertNobleNameToVipLevel(cmd.getPropName());
        for (Long userId : userIds) {
            PropCoupon propCoupon = new PropCoupon();
            propCoupon.setCouponNo(generateCouponNo());
            propCoupon.setUserId(userId);
            propCoupon.setCouponType(couponType);
            propCoupon.setPropId(Long.parseLong(cmd.getPropId()));
            propCoupon.setPropIcon(cmd.getPropIcon());
            propCoupon.setPropName(displayPropName);
            propCoupon.setValidDays(cmd.getValidDays());
            propCoupon.setPropDays(cmd.getPropDays());
            propCoupon.setStatus(PropCouponStatus.UNUSED);
            propCoupon.setSource(PropCouponSource.ADMIN_GRANT);
            propCoupon.setExpireTime(expireTime);
            propCoupon.setCreateTime(now);
            propCoupon.setUpdateTime(now);

            propCoupons.add(propCoupon);
        }

        return propCoupons;
    }

    /**
     * 转换贵族等级名称为VIP等级
     *
     * @param propName 道具名称
     * @return VIP等级名称
     */
    private String convertNobleNameToVipLevel(String propName) {
      if (propName == null) {
        return propName;
      }

      // 贵族等级映射
      switch (propName.toUpperCase()) {
        case "VISCOUNT":
          return "VIP 1";
        case "EARL":
          return "VIP 2";
        case "MARQUIS":
          return "VIP 3";
        case "DUKE":
          return "VIP 4";
        case "KING":
          return "VIP 5";
        default:
          return "";
      }
    }


  /**
     * 生成券编号（唯一）
     */
    private String generateCouponNo() {
        // 使用雪花算法生成唯一ID
        long id = IdWorkerUtils.getId();
        // 格式: COUPON + 时间戳 + ID
        return String.format("COUPON%d", id);
    }

    /**
     * 发送系统消息通知用户
     */
    private void sendNotifications(List<PropCoupon> propCoupons, Map<Long, UserProfile> userMap, PropCouponType couponType) {
        for (PropCoupon propCoupon : propCoupons) {
            try {
                UserProfile user = userMap.get(propCoupon.getUserId());
                if (user != null) {
                    sendNotification(propCoupon, user, couponType);
                }
            } catch (Exception e) {
                log.error("发送道具券通知失败, userId:{}, couponNo:{}", 
                        propCoupon.getUserId(), propCoupon.getCouponNo(), e);
            }
        }
    }

    /**
     * 发送单个通知
     */
    private void sendNotification(PropCoupon propCoupon, UserProfile user, PropCouponType couponType) {
        String content = getRegionContent(user.getId());
        officialNoticeClient.send(
            NoticeExtTemplateCustomizeCmd.builder()
                .toAccount(user.getId())
                .noticeType(OfficialNoticeTypeEnum.RECEIVE_PROP_COUPON)
                .content(content)
                .build()
        );

        log.info("发送道具券赠送通知, receiverId:{}, couponNo:{}, couponType:{}",
            user.getId(), propCoupon.getCouponNo(), propCoupon.getCouponType().getDesc());
    }

    private String getRegionContent(Long userId) {
        RegionConfig region = userRegionGateway.getRegionConfigByUserId(userId);
        boolean arRegion = region != null && "AR".equals(region.getRegionCode());
        if (arRegion) {
            return  "لقد تلقيت قسيمة. يرجى التحقق منها.";
        } else {
            return  "You have received a Coupon. Please check it.";
        }
    }
}
