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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.other.app.dto.clientobject.RocketRewardCO;
import com.red.circle.other.app.dto.cmd.RocketRewardClaimCmd;
import com.red.circle.other.app.manager.RocketImPushManager;
import com.red.circle.other.app.util.DistributedLockUtil;
import com.red.circle.other.domain.gateway.RocketConfigGateway;
import com.red.circle.other.domain.gateway.RocketHistoryGateway;
import com.red.circle.other.domain.gateway.RocketRewardLogGateway;
import com.red.circle.other.domain.gateway.RocketStatusGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.propcoupon.PropCouponSource;
import com.red.circle.other.domain.rocket.*;
import com.red.circle.other.infra.common.props.PropsSendCommon;
import com.red.circle.other.infra.database.cache.key.RocketKeys;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.inner.endpoint.material.props.BadgeBackpackClient;
import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribe;
import com.red.circle.other.inner.model.dto.material.PropsActivityRewardConfigInfoDTO;
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.enums.GoldOrigin;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 火箭奖励领取命令执行器
 *
 * @author system
 * @date 2025-01-15
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RocketRewardClaimCmdExe {

    private final RocketStatusGateway rocketStatusGateway;
    private final RocketConfigGateway rocketConfigGateway;
    private final RocketRewardLogGateway rocketRewardLogGateway;
    private final DistributedLockUtil distributedLockUtil;
    private final ImGroupClient imGroupClient;
    private final WalletGoldClient walletGoldClient;
    private final PropsBackpackClient propsBackpackClient;
    private final BadgeBackpackClient badgeBackpackClient;
    private final RocketHistoryGateway rocketHistoryGateway;
    private final PropsSendCommon propsSendCommon;
    private final UserProfileGateway userProfileGateway;
    private final RoomProfileManagerService roomProfileManagerService;
    private final Random random = new Random();

    /**
     * 执行奖励领取（同步）
     */
    public RocketRewardCO execute(RocketRewardClaimCmd cmd) {
        Long userId = cmd.getReqUserId();
        String lockKey = RocketKeys.LOCK.getKey(cmd.getRoomId(), "claim", userId);

        RocketRewardCO result = distributedLockUtil.executeWithLock(lockKey, 5L, () -> doClaim(cmd), "Reward collection operations are too frequent. Please try again later");

        if (result != null) {
            UserProfile userProfile = userProfileGateway.getByUserId(userId);
            String roomAccount = roomProfileManagerService.getRoomAccount(cmd.getRoomId());

            Map<String, Object> map = new HashMap<>();
            map.put("rewardType", result.getRewardType());
            map.put("cover", result.getCover());
            map.put("rocketLevel", result.getRocketLevel());
            map.put("userId", userId);
            map.put("userNickname", userProfile.getUserNickname());
            imGroupClient.sendCustomMessage(roomAccount,
                    CustomGroupMsgBodyCmd.builder()
                            .type(GroupMessageTypeEnum.ROCKET_REWARD_USER)
                            .data(map)
                            .build());
        }


        return result;
    }

    /**
     * 异步自动领取奖励（进房触发）
     * 无返回值，静默领取
     */
    @Async("rocketRewardExecutor")
    public void executeAsync(Long roomId, String roomAccount, Long userId, String userNickname) {
        try {
            log.debug("异步自动领取火箭奖励: roomId={}, userId={}", roomId, userId);
            
            RocketRewardClaimCmd cmd = new RocketRewardClaimCmd();
            cmd.setRoomId(roomId);
            cmd.setReqUserId(userId);

            execute(cmd);
        } catch (Exception e) {
            // 静默失败，不影响进房
            log.error("异步自动领取火箭奖励失败: roomId={}, userId={}", roomId, userId, e);
        }
    }

    /**
     * 执行领取逻辑
     */
    private RocketRewardCO doClaim(RocketRewardClaimCmd cmd) {
        Long userId = cmd.getReqUserId();
        Long roomId = cmd.getRoomId();
        String today = LocalDate.now().toString();

        // 1. 查询今天最新的火箭发射记录
        RocketHistory latestHistory = rocketHistoryGateway.findLatestByRoomIdAndDate(roomId, today);
        if (latestHistory == null) {
            return null;
        }

        Integer rocketLevel = latestHistory.getLevel();
        String historyId = latestHistory.getId();

        // 2. 检查是否已领取
        boolean hasClaimed = rocketRewardLogGateway.existsByRocketAndUser(historyId, userId);
        if (hasClaimed) {
            return null;
        }

        // 3. 查询火箭配置
        RocketConfig config = rocketConfigGateway.findByLevel(rocketLevel);
        if (config == null) {
            return null;
        }

        // 4. 检查库存（查询，不递增）
        int rewardQuantity = config.getRewardQuantity() != null ? config.getRewardQuantity() : 20;
        int currentClaimed = latestHistory.getClaimedCount() != null ? latestHistory.getClaimedCount() : 0;
        if (Boolean.TRUE.equals(cmd.getReceive()) && currentClaimed >= rewardQuantity) {
            return null;
        }

        // 5. 解析奖励配置
        List<PropsActivityRewardConfigInfoDTO> rewardList = parseRewardConfig(config.getRewardConfig().toJSONString());
        if (rewardList == null || rewardList.isEmpty()) {
            return null;
        }

        // 6. 随机选择一个奖励
        PropsActivityRewardConfigInfoDTO selectedReward = rewardList.get(random.nextInt(rewardList.size()));

        // 7. 递增领取人数（发放成功后再递增）
        try {
            rocketHistoryGateway.incrementClaimCount(historyId);
        } catch (Exception e) {
            log.error("递增领取人数失败: historyId={}", historyId, e);
        }

        // 8. 发放奖励（先发放）
        String walletBizNo = RocketRewardLog.buildBizNo(roomId.toString(), userId);
        grantReward(userId, selectedReward, walletBizNo);

        // 9. 保存领取记录
        saveRewardLog(roomId, userId, latestHistory, selectedReward, walletBizNo);

        // 10. 构建返回结果
        RocketRewardCO rewardCO = new RocketRewardCO();
        rewardCO.setRewardType(selectedReward.getType());
        rewardCO.setRewardItemId(selectedReward.getId());
        rewardCO.setRewardItemName(selectedReward.getName());
        rewardCO.setCover(selectedReward.getCover());
        rewardCO.setRewardAmount(getRewardAmount(selectedReward));
        rewardCO.setRocketLevel(rocketLevel);
        rewardCO.setMessage("恭喜您获得火箭奖励！");

        log.info("火箭奖励领取成功: roomId={}, userId={}, level={}, rewardType={}, rewardId={}", 
                roomId, userId, rocketLevel, selectedReward.getType(), selectedReward.getId());

        return rewardCO;
    }

    /**
     * 解析奖励配置
     */
    private List<PropsActivityRewardConfigInfoDTO> parseRewardConfig(String rewardConfigJson) {
        try {
            JSONArray jsonArray = JSON.parseArray(rewardConfigJson);
            return jsonArray.toJavaList(PropsActivityRewardConfigInfoDTO.class);
        } catch (Exception e) {
            log.error("解析奖励配置失败", e);
            return null;
        }
    }

    /**
     * 发放奖励
     */
    private void grantReward(Long userId, PropsActivityRewardConfigInfoDTO reward, String bizNo) {
        try {
            String rewardType = reward.getType();
            
            switch (rewardType) {
                case "GOLD" -> {
                    Long goldAmount = Long.parseLong(reward.getContent());
                    grantGold(userId, goldAmount, bizNo);
                }
                case "PROPS" -> {
                    Long propId = Long.parseLong(reward.getContent());
                    Integer quantity = reward.getQuantity() != null ? reward.getQuantity() : 1;
                    String detailType = reward.getDetailType();
                    grantProp(userId, propId, quantity, detailType, bizNo);
                }
                case "BADGE" -> {
                    Long badgeId = Long.parseLong(reward.getContent());
                    Integer quantity = reward.getQuantity() != null ? reward.getQuantity() : 1;
                    grantBadge(userId, badgeId, quantity, bizNo);
                }
                case "PROP_COUPON" -> {
                    Integer quantity = reward.getQuantity() != null ? reward.getQuantity() : 1;
                    grantPropCoupon(userId, reward.getContent(), quantity);
                }
                default -> throw new RuntimeException("不支持的奖励类型: " + rewardType);
            }
            
            log.info("奖励发放成功: userId={}, type={}, bizNo={}", userId, rewardType, bizNo);
            
        } catch (Exception e) {
            log.error("发放奖励失败: userId={}, reward={}", userId, reward, e);
            throw new RuntimeException("发放奖励失败: " + e.getMessage());
        }
    }

    /**
     * 发放金币
     */
    private void grantGold(Long userId, Long goldAmount, String bizNo) {
        try {
            BigDecimal amount = BigDecimal.valueOf(goldAmount);
            walletGoldClient.changeBalance(
                    GoldReceiptCmd.builder()
                            .opsIncome()
                            .userId(userId)
                            .eventId(bizNo)
                            .sysOrigin(SysOriginPlatformEnum.LIKEI)
                            .origin(GoldOrigin.ROCKET_REWARD)
                            .originDescribe(GoldOrigin.ROCKET_REWARD.name())
                            .amount(amount)
                            .remark("")
                            .build()
            );
        } catch (Exception e) {
            log.error("金币发放失败: userId={}, amount={}", userId, goldAmount, e);
            throw new RuntimeException("金币发放失败");
        }
    }


    /**
     * 发放道具
     */
    private void grantPropCoupon(Long userId, String propId, Integer quantity) {
        try {
            PrizeDescribe prizeDescribe = new PrizeDescribe();
            prizeDescribe.setContent(propId);
            prizeDescribe.setQuantity(quantity);
            propsSendCommon.sendPropCoupon(userId, PropCouponSource.ACTIVITY, List.of(prizeDescribe));
        } catch (Exception e) {
            log.error("道具券发放失败: userId={}, propId={}", userId, propId, e);
            throw new RuntimeException("道具券发放失败");
        }
    }

    /**
     * 发放道具
     */
    private void grantProp(Long userId, Long propId, Integer quantity, String detailType, String bizNo) {
        try {
            GivePropsBackpackCmd cmd = new GivePropsBackpackCmd()
                    .setAcceptUserId(userId)
                    .setPropsId(propId)
                    .setType(detailType)
                    .setOrigin(SendPropsOrigin.ROCKET_REWARD.name())
                    .setOriginDesc(SendPropsOrigin.ROCKET_REWARD.getDesc())
                    .setDays(quantity)
                    .setUseProps(Boolean.FALSE)
                    .setAllowGive(Boolean.FALSE)
                    .setOpUser(userId);

            propsBackpackClient.giveProps(cmd);
            propsBackpackClient.givePropsNotify(userId, detailType, quantity, propId);
        } catch (Exception e) {
            log.error("道具发放失败: userId={}, propId={}", userId, propId, e);
            throw new RuntimeException("道具发放失败");
        }
    }

    /**
     * 发放徽章
     */
    private void grantBadge(Long userId, Long badgeId, Integer quantity, String bizNo) {
        try {
            BadgeBackpackExpireTypeEnum expireType = quantity >= 1000
                    ? BadgeBackpackExpireTypeEnum.PERMANENT
                    : BadgeBackpackExpireTypeEnum.TEMPORARY;

            badgeBackpackClient.giveBadge(new GiveBadgeCmd()
                    .setAcceptUserId(userId)
                    .setBadgeId(badgeId)
                    .setExpireType(expireType)
                    .setDays(quantity)
                    .setBadgeType(SysBadgeConfigTypeEnum.ACTIVITY)
            );

            propsBackpackClient.givePropsNotify(userId, SysBadgeConfigTypeEnum.ACTIVITY.name(), quantity, badgeId);
        } catch (Exception e) {
            log.error("徽章发放失败: userId={}, badgeId={}", userId, badgeId, e);
            throw new RuntimeException("徽章发放失败");
        }
    }

    /**
     * 保存领取记录
     */
    private void saveRewardLog(Long roomId, Long userId, RocketHistory rocketHistory,
                               PropsActivityRewardConfigInfoDTO reward, String walletBizNo) {
        RocketRewardLog log = new RocketRewardLog();
        log.setRocketHistoryId(rocketHistory.getId());
        log.setRoomId(roomId);
        log.setUserId(userId);
        log.setRocketLevel(rocketHistory.getLevel());
        log.setIsContributor(0);
        log.setContributionEnergy(0L);
        log.setContributionRate(null);
        log.setRewardType(reward.getType());
        log.setRewardItemId(reward.getId());
        log.setRewardAmount(getRewardAmount(reward));
        log.setWalletBizNo(walletBizNo);
        log.setClaimedAt(LocalDateTime.now());
        log.setCreatedAt(LocalDateTime.now());
        
        rocketRewardLogGateway.save(log);
    }

    /**
     * 获取奖励金额
     */
    private Long getRewardAmount(PropsActivityRewardConfigInfoDTO reward) {
        if ("GOLD".equals(reward.getType())) {
            try {
                return Long.parseLong(reward.getContent());
            } catch (Exception e) {
                return 0L;
            }
        }
        int quantity = reward.getQuantity() == null ? 0 : reward.getQuantity();
        return Long.parseLong(Integer.toString(quantity));
    }
}
