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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.app.dto.cmd.SpinsTaskProgressUpdateCmd;
import com.red.circle.other.infra.database.cache.key.SpinsTaskKeys;
import com.red.circle.other.infra.database.rds.dao.task.SpinsTaskConfigDAO;
import com.red.circle.other.infra.database.rds.dao.task.SpinsUserTaskProgressDAO;
import com.red.circle.other.infra.database.rds.entity.task.SpinsTaskConfig;
import com.red.circle.other.infra.database.rds.entity.task.SpinsUserTaskProgress;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 更新任务进度执行器
 *
 * @author tf
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SpinsTaskProgressUpdateExe {

    private static final long TASK_CONFIG_CACHE_SECONDS = 3600L * 24;

    private final SpinsTaskConfigDAO spinsTaskConfigDAO;
    private final SpinsUserTaskProgressDAO spinsUserTaskProgressDAO;
    private final RedisService redisService;

    /**
     * 执行更新任务进度
     *
     * @param cmd 更新命令
     */
    @Transactional(rollbackFor = Exception.class)
    public void execute(SpinsTaskProgressUpdateCmd cmd) {
        Long userId = cmd.getUserId();
        String taskCode = cmd.getTaskCode();
        Integer progressValue = cmd.getProgressValue();

        // 1. 查询任务配置（带缓存）
        SpinsTaskConfig taskConfig = getTaskConfigWithCache(taskCode);
        if (taskConfig == null) {
            log.warn("任务配置不存在或已下线, taskCode={}", taskCode);
            return;
        }

        // 2. 获取或创建用户任务进度
        String cycleKey = getCurrentCycleKey(taskConfig.getCycleType());
        SpinsUserTaskProgress progress = getOrCreateProgress(userId, taskConfig, cycleKey);

        // 3. 任务已完成/已领取，跳过
        if (progress.getTaskStatus() != null && progress.getTaskStatus() >= 1) {
            return;
        }

        // 4. 进度未推进，跳过（防乱序回退）
        if (progressValue <= progress.getCurrentValue()) {
            return;
        }

        // 5. 更新进度
        progress.setCurrentValue(progressValue);
        progress.setProgressRate(calculateProgressRate(progressValue, taskConfig.getTargetValue()));
        progress.setUpdateTime(new Timestamp(System.currentTimeMillis()));

        if (progressValue >= taskConfig.getTargetValue() && progress.getTaskStatus() == 0) {
            progress.setTaskStatus(1);
            progress.setCompleteTime(new Timestamp(System.currentTimeMillis()));
            progress.setCompleteTimes(progress.getCompleteTimes() + 1);
            log.info("任务完成, userId={}, taskCode={}", userId, taskCode);
        }

        spinsUserTaskProgressDAO.updateById(progress);
    }


    // ---- private ----

    private SpinsTaskConfig getTaskConfigWithCache(String taskCode) {
        String cacheKey = SpinsTaskKeys.SPINS_TASK_CONFIG.getKey(taskCode);
        String cached = redisService.getString(cacheKey);
        if (StringUtils.isNotBlank(cached)) {
            return JacksonUtils.readValue(cached, new TypeReference<>() {});
        }

        List<SpinsTaskConfig> list = spinsTaskConfigDAO.selectList(
                new LambdaQueryWrapper<SpinsTaskConfig>()
                        .eq(SpinsTaskConfig::getTaskCode, taskCode)
                        .eq(SpinsTaskConfig::getStatus, 1)
                        .last("limit 1")
        );

        if (list.isEmpty()) {
            return null;
        }

        SpinsTaskConfig config = list.get(0);
        redisService.setString(cacheKey, JacksonUtils.toJson(config), TASK_CONFIG_CACHE_SECONDS, TimeUnit.SECONDS);
        return config;
    }

    private SpinsUserTaskProgress getOrCreateProgress(Long userId, SpinsTaskConfig taskConfig, String cycleKey) {
        List<SpinsUserTaskProgress> list = spinsUserTaskProgressDAO.selectList(
                new LambdaQueryWrapper<SpinsUserTaskProgress>()
                        .eq(SpinsUserTaskProgress::getUserId, userId)
                        .eq(SpinsUserTaskProgress::getTaskCode, taskConfig.getTaskCode())
                        .eq(SpinsUserTaskProgress::getCycleKey, cycleKey)
        );

        if (!list.isEmpty()) {
            return list.get(0);
        }

        SpinsUserTaskProgress progress = buildNewProgress(userId, taskConfig, cycleKey);
        try {
            spinsUserTaskProgressDAO.insert(progress);
        } catch (DuplicateKeyException e) {
            // 并发插入，重新查询返回已存在记录
            List<SpinsUserTaskProgress> retryList = spinsUserTaskProgressDAO.selectList(
                    new LambdaQueryWrapper<SpinsUserTaskProgress>()
                            .eq(SpinsUserTaskProgress::getUserId, userId)
                            .eq(SpinsUserTaskProgress::getTaskCode, taskConfig.getTaskCode())
                            .eq(SpinsUserTaskProgress::getCycleKey, cycleKey)
            );
            return retryList.get(0);
        }
        return progress;
    }

    private SpinsUserTaskProgress buildNewProgress(Long userId, SpinsTaskConfig taskConfig, String cycleKey) {
        SpinsUserTaskProgress progress = new SpinsUserTaskProgress();
        progress.setUserId(userId);
        progress.setTaskId(taskConfig.getId());
        progress.setTaskCode(taskConfig.getTaskCode());
        progress.setCurrentValue(0);
        progress.setTargetValue(taskConfig.getTargetValue());
        progress.setProgressRate(BigDecimal.ZERO);
        progress.setTaskStatus(0);
        progress.setCycleType(taskConfig.getCycleType());
        progress.setCycleDate(LocalDate.now());
        progress.setCycleKey(cycleKey);
        progress.setCompleteTimes(0);
        progress.setRewardType(taskConfig.getRewardType());
        progress.setRewardValue(taskConfig.getRewardValue());
        Timestamp now = new Timestamp(System.currentTimeMillis());
        progress.setCreateTime(now);
        progress.setUpdateTime(now);
        return progress;
    }

    private BigDecimal calculateProgressRate(Integer currentValue, Integer targetValue) {
        if (targetValue == null || targetValue == 0) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(currentValue)
                .divide(BigDecimal.valueOf(targetValue), 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100))
                .setScale(2, RoundingMode.HALF_UP);
    }

    private String getCurrentCycleKey(Integer cycleType) {
        LocalDate now = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
        if (cycleType == 1) {
            return now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        } else if (cycleType == 2) {
            int week = now.get(WeekFields.ISO.weekOfWeekBasedYear());
            return now.getYear() + "W" + String.format("%02d", week);
        } else {
            return "ONCE";
        }
    }
}
