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

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.clientobject.activity.ClaimRewardResultCO;
import com.red.circle.other.app.dto.cmd.activity.ClaimActivityRewardCmd;
import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway;
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
import com.red.circle.other.infra.database.rds.dao.activity.UserActivityRewardClaimDAO;
import com.red.circle.other.infra.database.rds.entity.activity.LotteryDeviceRecord;
import com.red.circle.other.infra.database.rds.entity.activity.UserActivityRewardClaim;
import com.red.circle.other.infra.database.rds.service.activity.LotteryDeviceRecordService;
import com.red.circle.other.infra.database.rds.service.activity.UserActivityRechargeService;
import com.red.circle.other.infra.database.rds.service.user.device.LatestMobileDeviceService;
import com.red.circle.other.inner.asserts.OtherErrorCode;
import com.red.circle.other.inner.endpoint.activity.LotteryTicketClient;
import com.red.circle.other.inner.endpoint.activity.PropsActivityClient;
import com.red.circle.other.inner.enums.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.model.cmd.activity.SendActivityRewardCmd;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsGroup;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.other.inner.asserts.DynamicErrorCode;
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardProps;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 领取活动奖励执行器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ClaimActivityRewardExe {

    private final ActivitySourceGroupGateway activitySourceGroupGateway;
    private final UserActivityRechargeService userActivityRechargeService;
    private final UserActivityRewardClaimDAO userActivityRewardClaimDAO;
    private final LotteryTicketClient lotteryTicketClient;
    private final RegisterDeviceGateway registerDeviceGateway;
    private final LotteryDeviceRecordService lotteryDeviceRecordService;
    private final PropsActivityClient propsActivityClient;

    @Transactional(rollbackFor = Exception.class)
    public ClaimRewardResultCO execute(ClaimActivityRewardCmd cmd) {
        PropsActivityTypeEnum activityType = PropsActivityTypeEnum.valueOf(cmd.getActivityType());
        SysOriginPlatformEnum sysOrigin = SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin());

        // 2. 校验设备唯一性（防止小号薅羊毛）
        checkDeviceUnique(cmd.getActivityId(), cmd.getReqUserId());

        List<ActivityPropsRule> ruleConfigs = activitySourceGroupGateway.listRule(sysOrigin, activityType);
        ActivityPropsRule targetRule = ruleConfigs.stream()
            .filter(rule -> Objects.equals(rule.getId(), cmd.getRuleId()))
            .findFirst()
            .orElse(null);

        ResponseAssert.notNull(DynamicErrorCode.NO_RELATED_INFORMATION_FOUND, targetRule);

        long count = userActivityRewardClaimDAO.selectCount(
            new LambdaQueryWrapper<UserActivityRewardClaim>()
                .eq(UserActivityRewardClaim::getUserId, cmd.getReqUserId())
                .eq(UserActivityRewardClaim::getActivityId, cmd.getActivityId())
                .eq(UserActivityRewardClaim::getRuleId, cmd.getRuleId())
        );

        ResponseAssert.isFalse(CommonErrorCode.REPEATED_SUBMISSION, count > 0);

        BigDecimal requiredAmount = parseRequiredAmount(targetRule.getJsonData());
        ResponseAssert.notNull(DynamicErrorCode.NO_RELATED_INFORMATION_FOUND, requiredAmount);

        BigDecimal userTotalAmount = userActivityRechargeService.getUserTotalAmount(
            cmd.getReqUserId(),
                cmd.getActivityId()
        );

        ResponseAssert.isFalse(OtherErrorCode.USER_RECHARGE_INVALID, Objects.isNull(userTotalAmount) || userTotalAmount.compareTo(requiredAmount) < 0);

//        Integer ticketCount = parseTicketCount(targetRule.getJsonData());
//        if (Objects.isNull(ticketCount) || ticketCount <= 0) {
//            ticketCount = 1;
//        }

//        List<Long> ticketIds = new ArrayList<>();
//        for (int i = 0; i < ticketCount; i++) {
//            Long ticketId = ResponseAssert.requiredSuccess(
//                lotteryTicketClient.addTicket(
//                    cmd.getReqUserId(),
//                        cmd.getActivityId(),
//                    1,
//                    "ACTIVITY_REWARD",
//                    cmd.getRuleId().toString()
//                )
//            );
//            ticketIds.add(ticketId);
//        }

        UserActivityRewardClaim claim = new UserActivityRewardClaim()
            .setUserId(cmd.getReqUserId())
            .setActivityId(cmd.getActivityId())
            .setRuleId(cmd.getRuleId())
            .setActivityType(cmd.getActivityType())
            .setStatus(1)
//            .setLotteryTicketIds(String.join(",", ticketIds.stream().map(String::valueOf).toArray(String[]::new)))
            .setCreateTime(TimestampUtils.now())
            .setUpdateTime(TimestampUtils.now());

        userActivityRewardClaimDAO.insert(claim);

        ActivityPropsGroup group = activitySourceGroupGateway.getActivityPropsGroup(
                sysOrigin.name(),
                targetRule.getResourceGroupId()
        );

        List<ActivityRewardProps> activityRewardProps = group.getActivityRewardProps();
        ResponseAssert.isFalse(DynamicErrorCode.NO_RELATED_INFORMATION_FOUND, activityRewardProps.isEmpty());
        ActivityRewardProps rewardProps = activityRewardProps.get(0);

        propsActivityClient.sendActivityReward(new SendActivityRewardCmd()
                .setTrackId(cmd.getActivityId())
                .setOrigin(SendPropsOrigin.LUCK_DOLLAR_REWARD)
                .setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()))
                .setSourceGroupId(rewardProps.getGroupId())
                .setAcceptUserId(cmd.getReqUserId())
        );

        return new ClaimRewardResultCO()
            .setSuccess(true)
//            .setLotteryTicketIds(ticketIds)
                ;
    }

    /**
     * 校验设备唯一性（防止小号薅羊毛）
     */
    private void checkDeviceUnique(Long activityId, Long userId) {
        String fingerprint = registerDeviceGateway.getEnhancedDeviceFingerprintByUserId(userId);

        if (com.red.circle.tool.core.text.StringUtils.isBlank(fingerprint)) {
            log.warn("Failed to get device fingerprint, userId: {}", userId);
            return;
        }

        LotteryDeviceRecord existingRecord = lotteryDeviceRecordService
                .getByActivityAndFingerprint(activityId, fingerprint);

        if (existingRecord != null && !existingRecord.getUserId().equals(userId)) {
            log.warn("Duplicate device detected, activityId: {}, currentUser: {}, boundUser: {}, fingerprint: {}",
                    activityId, userId, existingRecord.getUserId(), fingerprint);
            ResponseAssert.isTrue(OtherErrorCode.DEVICE_ALREADY_USED, false);
        }
    }

    private BigDecimal parseRequiredAmount(String jsonData) {
        if (StrUtil.isBlank(jsonData)) {
            return null;
        }

        try {
            Map<String, Object> dataMap = JacksonUtils.readValue(jsonData, Map.class);
            Object quantity = dataMap.get("quantity");
            if (Objects.isNull(quantity)) {
                return null;
            }

            if (quantity instanceof Number) {
                return new BigDecimal(quantity.toString());
            }

            if (quantity instanceof String) {
                return new BigDecimal((String) quantity);
            }

            return null;
        } catch (Exception e) {
            log.error("parse jsonData error: {}", jsonData, e);
            return null;
        }
    }

    private Integer parseTicketCount(String jsonData) {
        if (StrUtil.isBlank(jsonData)) {
            return 1;
        }

        try {
            Map<String, Object> dataMap = JacksonUtils.readValue(jsonData, Map.class);
            Object ticketCount = dataMap.get("ticketCount");
            if (Objects.isNull(ticketCount)) {
                return 1;
            }

            if (ticketCount instanceof Number) {
                return ((Number) ticketCount).intValue();
            }

            if (ticketCount instanceof String) {
                return Integer.parseInt((String) ticketCount);
            }

            return 1;
        } catch (Exception e) {
            log.error("parse ticketCount error: {}", jsonData, e);
            return 1;
        }
    }
}
