package com.dora.manager;

import com.dora.entity.RevenueConfig;
import com.dora.entity.User;
import com.dora.event.PromotionLevelChangedEvent;
import com.dora.mapper.RevenueConfigMapper;
import com.dora.mapper.UserMapper;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Singular;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 推广等级统一管理器
 * 解决多个地方更新推广等级的问题
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class PromotionLevelManager {

    private final UserMapper userMapper;
    private final RevenueConfigMapper revenueConfigMapper;
    private final ApplicationEventPublisher eventPublisher;

    @Autowired
    @Lazy
    private PromotionLevelManager self;

    // 用户级别的锁，防止并发更新同一用户
    private final ConcurrentHashMap<Long, Lock> userLocks = new ConcurrentHashMap<>();

    /**
     * 推广等级计算策略枚举
     */
    public enum CalculationStrategy {
        PAID_FANS_ONLY("paid_fans", "仅付费粉丝数"),
        TOTAL_FANS_ONLY("total_fans", "仅总粉丝数"),
        WEIGHTED_AVERAGE("weighted", "加权平均");
        
        @Getter
        private final String code;
        @Getter
        private final String description;
        
        CalculationStrategy(String code, String description) {
            this.code = code;
            this.description = description;
        }
    }

    /**
     * 推广等级更新结果
     */
    @Getter
    @Builder
    public static class PromotionLevelUpdateResult {
        private final boolean updated;
        private final Integer oldLevel;
        private final Integer newLevel;
        private final String triggerSource;
        private final LocalDateTime updateTime;
    }

    /**
     * 统一的推广等级更新方法
     * 
     * @param userId 用户ID
     * @param strategy 计算策略
     * @param triggerSource 触发源
     * @return 更新结果
     */
    @Transactional
    public PromotionLevelUpdateResult updatePromotionLevel(
            Long userId, 
            CalculationStrategy strategy, 
            String triggerSource) {
        
        Lock userLock = userLocks.computeIfAbsent(userId, k -> new ReentrantLock());
        userLock.lock();
        
        try {
            log.info("开始更新用户推广等级 - userId: {}, strategy: {}, source: {}",
                    userId, strategy.getCode(), triggerSource);
            
            // 1. 获取用户当前信息
            User user = userMapper.selectById(userId);
            if (user == null) {
                log.warn("用户不存在 - userId: {}", userId);
                return createFailedResult(triggerSource);
            }
            
            Integer currentLevel = user.getPromotionLevel();
            
            // 2. 根据策略计算新等级
            int newLevel = calculateLevelByStrategy(user, strategy);
            
            // 3. 检查等级是否发生变化
            if (Objects.equals(currentLevel, newLevel)) {
                log.debug("用户推广等级未发生变化 - userId: {}, level: {}", userId, currentLevel);
                return createUnchangedResult(currentLevel, triggerSource);
            }
            
            // 4. 更新数据库
            int updatedRows = userMapper.updatePromotionLevel(userId, newLevel);
            if (updatedRows == 0) {
                log.warn("更新用户推广等级失败，可能存在并发更新 - userId: {}", userId);
                //可以选择返回失败或者重新尝试
                return createFailedResult(triggerSource);
            }
            
            // 5. 记录审计日志
            recordAuditLog(userId, currentLevel, newLevel, strategy, triggerSource);
            
            // 6. 发布等级变化事件
            publishLevelChangedEvent(userId, currentLevel, newLevel, triggerSource);
            
            log.info("用户推广等级更新成功 - userId: {}, level: {} -> {}, strategy: {}, source: {}",
                    userId, currentLevel, newLevel, strategy.getCode(), triggerSource);
            
            return createSuccessResult(currentLevel, newLevel, triggerSource);
            
        } finally {
            userLock.unlock();
            // 当锁没有其他等待线程时，从map中移除，避免内存泄漏
            userLocks.compute(userId, (k, v) -> (v != null && v.hasQueuedThreads()) ? v : null);
        }
    }

    /**
     * 根据策略计算推广等级
     */
    private int calculateLevelByStrategy(User user, CalculationStrategy strategy) {
        int fansCount;
        Long userId = user.getId();
        
        switch (strategy) {
            case TOTAL_FANS_ONLY:
                fansCount = userMapper.countFansByInviterId(userId);
                break;
            case WEIGHTED_AVERAGE:
                // 加权策略：付费粉丝权重 * 2 + 总粉丝权重 * 1
                // TODO: 可以优化为一次数据库查询返回两个字段
                int paidFans = userMapper.countPaidFansByInviterId(userId);
                int totalFans = userMapper.countFansByInviterId(userId);
                fansCount = (paidFans * 2) + totalFans;
                break;
            case PAID_FANS_ONLY:
            default:
                fansCount = userMapper.countPaidFansByInviterId(userId);
        }
        
        return calculatePromotionLevel(fansCount);
    }

    /**
     * 根据粉丝数计算推广等级
     */
    private int calculatePromotionLevel(int fansCount) {
        try {
            List<RevenueConfig> configs = self.getPromotionRevenueConfigs();
            
            return configs.stream()
                    .filter(config -> config.getMinFans() != null && fansCount >= config.getMinFans())
                    .mapToInt(RevenueConfig::getLevel)
                    .max()
                    .orElse(1);
            
        } catch (Exception e) {
            log.error("计算推广等级失败 - fansCount: {}", fansCount, e);
            return 1; // 默认返回1级
        }
    }

    /**
     * 获取并缓存推广收益配置
     */
    @Cacheable("revenueConfigs")
    public List<RevenueConfig> getPromotionRevenueConfigs() {
        return revenueConfigMapper.selectByConfigType("promotion");
    }

    /**
     * 记录审计日志
     */
    private void recordAuditLog(Long userId, Integer oldLevel, Integer newLevel, 
                              CalculationStrategy strategy, String triggerSource) {
        // 这里可以记录到专门的审计日志表
        String strategyCode = (strategy != null) ? strategy.getCode() : "admin_override";
        log.info("推广等级变化审计 - userId: {}, level: {} -> {}, strategy: {}, source: {}, time: {}",
                userId, oldLevel, newLevel, strategyCode, triggerSource, LocalDateTime.now());
    }

    /**
     * 发布等级变化事件
     */
    private void publishLevelChangedEvent(Long userId, Integer oldLevel, Integer newLevel, String triggerSource) {
        try {
            PromotionLevelChangedEvent event = new PromotionLevelChangedEvent(
                    this, userId, oldLevel, newLevel, triggerSource);
            eventPublisher.publishEvent(event);
        } catch (Exception e) {
            log.error("发布推广等级变化事件失败 - userId: {}", userId, e);
        }
    }

    /**
     * 批量更新推广等级（管理员操作）
     */
    @Transactional
    public BatchUpdateResult batchUpdatePromotionLevel(List<Long> userIds, Integer targetLevel, String adminId) {
        log.info("管理员批量更新推广等级 - adminId: {}, userCount: {}, targetLevel: {}",
                adminId, userIds.size(), targetLevel);

        BatchUpdateResult.BatchUpdateResultBuilder resultBuilder = BatchUpdateResult.builder();
        
        if (userIds == null || userIds.isEmpty()) {
            return resultBuilder.build();
        }

        List<User> users = userMapper.selectBatchIds(userIds);
        Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, user -> user));

        for (Long userId : userIds) {
            User user = userMap.get(userId);
            if (user == null) {
                resultBuilder.failedUser(userId, "用户不存在");
                continue;
            }

            Integer oldLevel = user.getPromotionLevel();
            if (Objects.equals(oldLevel, targetLevel)) {
                resultBuilder.skippedUser(userId, "等级未变化");
                continue;
            }

            int updatedRows = userMapper.updatePromotionLevel(userId, targetLevel);
            if (updatedRows > 0) {
                resultBuilder.successUser(userId);
                recordAuditLog(userId, oldLevel, targetLevel, null, "admin_batch_update:" + adminId);
                publishLevelChangedEvent(userId, oldLevel, targetLevel, "admin_batch_update");
            } else {
                resultBuilder.failedUser(userId, "数据库更新失败");
            }
        }

        BatchUpdateResult result = resultBuilder.build();
        log.info("批量更新推广等级完成 - 成功: {}, 失败: {}, 跳过: {}",
                result.getSuccessCount(), result.getFailedCount(), result.getSkippedCount());
        
        return result;
    }

    // 辅助方法创建结果对象
    private PromotionLevelUpdateResult createSuccessResult(Integer oldLevel, Integer newLevel, String triggerSource) {
        return PromotionLevelUpdateResult.builder()
                .updated(true)
                .oldLevel(oldLevel)
                .newLevel(newLevel)
                .triggerSource(triggerSource)
                .updateTime(LocalDateTime.now())
                .build();
    }

    private PromotionLevelUpdateResult createUnchangedResult(Integer level, String triggerSource) {
        return PromotionLevelUpdateResult.builder()
                .updated(false)
                .oldLevel(level)
                .newLevel(level)
                .triggerSource(triggerSource)
                .updateTime(LocalDateTime.now())
                .build();
    }

    private PromotionLevelUpdateResult createFailedResult(String triggerSource) {
        return PromotionLevelUpdateResult.builder()
                .updated(false)
                .triggerSource(triggerSource)
                .updateTime(LocalDateTime.now())
                .build();
    }

    /**
     * 批量更新结果
     */
    @Getter
    @Builder
    public static class BatchUpdateResult {
        @Singular
        private final List<Long> successUsers;
        @Singular
        private final Map<Long, String> failedUsers;
        @Singular
        private final Map<Long, String> skippedUsers;

        public int getSuccessCount() { return successUsers.size(); }
        public int getFailedCount() { return failedUsers.size(); }
        public int getSkippedCount() { return skippedUsers.size(); }
    }
}
