package com.fs.course.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fs.common.utils.StringUtils;
import com.fs.course.config.CourseRandomRedPacketConfig;
import com.fs.course.config.CourseRandomRedPacketTier;
import com.fs.course.config.RandomRedPacketConfig;
import com.fs.course.config.RandomRedPacketRule;
import com.fs.course.domain.FsUserCourse;
import com.fs.course.domain.FsUserCourseVideo;
import com.fs.course.mapper.FsUserCourseMapper;
import com.fs.course.param.CourseRandomRedPacketBatchSaveParam;
import com.fs.course.param.CourseRandomRedPacketCopyParam;
import com.fs.course.service.ICourseRandomRedPacketService;
import com.fs.system.domain.SysConfig;
import com.fs.system.service.ISysConfigService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 课程随机红包配置服务实现，对应 fs_user_course.project 字段
 */
@Service
public class CourseRandomRedPacketServiceImpl implements ICourseRandomRedPacketService {

    private static final Logger logger = LoggerFactory.getLogger(CourseRandomRedPacketServiceImpl.class);

    private static final String CONFIG_KEY_PREFIX = "course.randomRedPacket:project:";

    @Autowired
    private FsUserCourseMapper fsUserCourseMapper;

    @Autowired
    private ISysConfigService configService;

    @Override
    public CourseRandomRedPacketConfig getConfigByProject(Long project) {
        if (project == null) {
            return null;
        }
        String json = configService.selectConfigByKey(buildConfigKey(project));
        if (StringUtils.isBlank(json)) {
            return null;
        }
        try {
            CourseRandomRedPacketConfig config = JSONUtil.toBean(json, CourseRandomRedPacketConfig.class);
            if (config != null) {
                config.setProject(project);
            }
            return config;
        } catch (Exception e) {
            logger.error("解析课程随机红包配置失败 project={}", project, e);
            return null;
        }
    }

    @Override
    public void saveConfig(CourseRandomRedPacketConfig config) {
        validateConfig(config);
        doSaveConfig(config);
    }

    @Override
    public void batchSaveConfig(CourseRandomRedPacketBatchSaveParam param) {
        if (param == null || CollectionUtils.isEmpty(param.getProjects())) {
            throw new IllegalArgumentException("课程项目不能为空");
        }
        CourseRandomRedPacketConfig template = buildConfigFromBatchParam(param);
        validateConfig(template);
        for (Long project : param.getProjects()) {
            if (project == null) {
                continue;
            }
            CourseRandomRedPacketConfig config = copyConfigFields(template);
            config.setProject(project);
            doSaveConfig(config);
        }
    }

    @Override
    public void copyConfig(CourseRandomRedPacketCopyParam param) {
        if (param == null || param.getSourceProject() == null) {
            throw new IllegalArgumentException("源课程项目不能为空");
        }
        if (CollectionUtils.isEmpty(param.getTargetProjects())) {
            throw new IllegalArgumentException("目标课程项目不能为空");
        }
        CourseRandomRedPacketConfig sourceConfig = getConfigByProject(param.getSourceProject());
        if (sourceConfig == null) {
            throw new IllegalArgumentException("源课程项目未配置红包规则");
        }
        for (Long targetProject : param.getTargetProjects()) {
            if (targetProject == null || targetProject.equals(param.getSourceProject())) {
                continue;
            }
            CourseRandomRedPacketConfig config = copyConfigFields(sourceConfig);
            config.setProject(targetProject);
            doSaveConfig(config);
        }
    }

    @Override
    public List<CourseRandomRedPacketConfig> listConfiguredProjects() {
        SysConfig query = new SysConfig();
        query.setConfigKey(CONFIG_KEY_PREFIX);
        List<SysConfig> configList = configService.selectConfigList(query);
        if (CollectionUtils.isEmpty(configList)) {
            return new ArrayList<>();
        }
        List<CourseRandomRedPacketConfig> result = new ArrayList<>();
        for (SysConfig sysConfig : configList) {
            if (sysConfig == null || StringUtils.isBlank(sysConfig.getConfigKey())
                    || !sysConfig.getConfigKey().startsWith(CONFIG_KEY_PREFIX)) {
                continue;
            }
            String projectStr = sysConfig.getConfigKey().substring(CONFIG_KEY_PREFIX.length());
            try {
                Long project = Long.valueOf(projectStr);
                CourseRandomRedPacketConfig config = getConfigByProject(project);
                if (config != null) {
                    result.add(config);
                }
            } catch (NumberFormatException e) {
                logger.warn("忽略无效的课程红包配置键 configKey={}", sysConfig.getConfigKey());
            }
        }
        return result;
    }

    private void doSaveConfig(CourseRandomRedPacketConfig config) {
        String configKey = buildConfigKey(config.getProject());
        String json = JSONUtil.toJsonStr(config);
        SysConfig existing = configService.selectConfigByConfigKey(configKey);
        if (existing != null) {
            existing.setConfigValue(json);
            configService.updateConfig(existing);
            return;
        }
        SysConfig sysConfig = new SysConfig();
        sysConfig.setConfigName("课程红包配置-" + config.getProject());
        sysConfig.setConfigKey(configKey);
        sysConfig.setConfigValue(json);
        sysConfig.setConfigType("N");
        configService.insertConfig(sysConfig);
    }

    private CourseRandomRedPacketConfig buildConfigFromBatchParam(CourseRandomRedPacketBatchSaveParam param) {
        CourseRandomRedPacketConfig config = new CourseRandomRedPacketConfig();
        config.setProject(param.getProjects().get(0));
        config.setEnableRandom(param.getEnableRandom());
        config.setCompanyWhitelist(param.getCompanyWhitelist());
        config.setTiers(param.getTiers());
        return config;
    }

    private CourseRandomRedPacketConfig copyConfigFields(CourseRandomRedPacketConfig source) {
        CourseRandomRedPacketConfig config = new CourseRandomRedPacketConfig();
        config.setEnableRandom(source.getEnableRandom());
        config.setCompanyWhitelist(source.getCompanyWhitelist());
        config.setTiers(source.getTiers());
        return config;
    }

    private void validateConfig(CourseRandomRedPacketConfig config) {
        if (config == null || config.getProject() == null) {
            throw new IllegalArgumentException("课程项目不能为空");
        }
        if (!Boolean.TRUE.equals(config.getEnableRandom()) || CollectionUtils.isEmpty(config.getTiers())) {
            return;
        }
        int totalWeight = config.getTiers().stream()
                .filter(t -> t.getWeight() != null && t.getWeight() > 0)
                .mapToInt(CourseRandomRedPacketTier::getWeight)
                .sum();
        if (totalWeight != 100) {
            throw new IllegalArgumentException("权重总和必须为100%");
        }
    }

    @Override
    public BigDecimal resolveAmount(BigDecimal baseAmount, Long courseId, Long companyId,
                                    FsUserCourseVideo video, boolean allowLegacyRandom) {
        BigDecimal safeBase = baseAmount == null ? BigDecimal.ZERO : baseAmount;
        try {
            Long project = resolveProject(courseId, video);
            if (project == null) {
                return fallbackAmount(safeBase, video, allowLegacyRandom);
            }
            CourseRandomRedPacketConfig config = getConfigByProject(project);
            if (config != null && Boolean.TRUE.equals(config.getEnableRandom())
                    && CollectionUtils.isNotEmpty(config.getTiers())) {
                if (isCompanyInWhitelist(config.getCompanyWhitelist(), companyId)) {
                    BigDecimal randomAmount = pickAmountByTiers(config.getTiers());
                    if (randomAmount.compareTo(BigDecimal.ZERO) > 0) {
                        logger.info("课程随机红包生效 project={}, courseId={}, companyId={}, amount={}",
                                project, courseId, companyId, randomAmount);
                        return randomAmount;
                    }
                }
                return safeBase;
            }
            return fallbackAmount(safeBase, video, allowLegacyRandom);
        } catch (Exception e) {
            logger.error("红包金额计算异常，使用基础金额兜底 courseId={}, companyId={}, baseAmount={}",
                    courseId, companyId, safeBase, e);
            return safeBase;
        }
    }

    private BigDecimal fallbackAmount(BigDecimal safeBase, FsUserCourseVideo video, boolean allowLegacyRandom) {
        if (allowLegacyRandom && video != null) {
            return applyLegacyRandomRedPacket(safeBase, video);
        }
        return safeBase;
    }

    /**
     * 通过 courseId 或 video.courseId 查询 fs_user_course.project
     */
    private Long resolveProject(Long courseId, FsUserCourseVideo video) {
        Long targetCourseId = courseId;
        if (targetCourseId == null && video != null) {
            targetCourseId = video.getCourseId();
        }
        if (targetCourseId == null) {
            return null;
        }
        FsUserCourse course = fsUserCourseMapper.selectFsUserCourseByCourseId(targetCourseId);
        return course == null ? null : course.getProject();
    }

    private String buildConfigKey(Long project) {
        return CONFIG_KEY_PREFIX + project;
    }

    private boolean isCompanyInWhitelist(String whitelist, Long companyId) {
        if (StringUtils.isBlank(whitelist) || "ALL".equalsIgnoreCase(whitelist.trim())) {
            return true;
        }
        if (companyId == null) {
            return false;
        }
        String companyIdStr = String.valueOf(companyId);
        for (String item : whitelist.split(",")) {
            if (companyIdStr.equals(item.trim())) {
                return true;
            }
        }
        return false;
    }

    private BigDecimal pickAmountByTiers(List<CourseRandomRedPacketTier> tiers) {
        int totalWeight = tiers.stream()
                .filter(t -> t.getWeight() != null && t.getWeight() > 0 && t.getAmount() != null)
                .mapToInt(CourseRandomRedPacketTier::getWeight)
                .sum();
        if (totalWeight <= 0) {
            return BigDecimal.ZERO;
        }
        int randomPoint = new Random().nextInt(totalWeight);
        int currentWeight = 0;
        for (CourseRandomRedPacketTier tier : tiers) {
            if (tier.getWeight() == null || tier.getWeight() <= 0 || tier.getAmount() == null) {
                continue;
            }
            currentWeight += tier.getWeight();
            if (randomPoint < currentWeight) {
                return tier.getAmount().setScale(2, RoundingMode.HALF_UP);
            }
        }
        return tiers.get(0).getAmount().setScale(2, RoundingMode.HALF_UP);
    }

    private BigDecimal applyLegacyRandomRedPacket(BigDecimal baseAmount, FsUserCourseVideo video) {
        String json = configService.selectConfigByKey("randomRedpacket:config");
        if (StringUtils.isBlank(json)) {
            return baseAmount;
        }
        RandomRedPacketConfig randomRedPacket = JSONUtil.toBean(json, RandomRedPacketConfig.class);
        if (randomRedPacket == null || !Boolean.TRUE.equals(randomRedPacket.getEnableRandomRedpacket())) {
            return baseAmount;
        }
        BigDecimal randomMoney = BigDecimal.ZERO;
        String randomRedPacketRules = video.getRandomRedPacketRules();
        if (StringUtils.isNotBlank(randomRedPacketRules)) {
            JSONArray array = JSONObject.parseArray(randomRedPacketRules);
            List<RandomRedPacketRule> rules = new ArrayList<>();
            for (Object o : array) {
                rules.add(JSONObject.toJavaObject((JSONObject) o, RandomRedPacketRule.class));
            }
            randomMoney = getRandomMoneyByLegacyRules(rules);
        } else if (CollectionUtils.isNotEmpty(randomRedPacket.getRules())) {
            randomMoney = getRandomMoneyByLegacyRules(randomRedPacket.getRules());
        }
        if (randomMoney.compareTo(BigDecimal.ZERO) > 0) {
            return randomMoney;
        }
        return baseAmount;
    }

    private BigDecimal getRandomMoneyByLegacyRules(List<RandomRedPacketRule> rules) {
        try {
            if (CollectionUtils.isEmpty(rules)) {
                return BigDecimal.ZERO;
            }
            int totalWeight = rules.stream()
                    .mapToInt(RandomRedPacketRule::getWeight)
                    .sum();
            if (totalWeight <= 0) {
                return BigDecimal.ZERO;
            }
            Random random = new Random();
            int randomPoint = random.nextInt(totalWeight);
            RandomRedPacketRule selectedRule = null;
            int currentWeight = 0;
            for (RandomRedPacketRule rule : rules) {
                currentWeight += rule.getWeight();
                if (randomPoint < currentWeight) {
                    selectedRule = rule;
                    break;
                }
            }
            if (selectedRule == null) {
                selectedRule = rules.get(0);
            }
            BigDecimal maxAmount = selectedRule.getMaxAmount();
            BigDecimal minAmount = selectedRule.getMinAmount();
            if (maxAmount.compareTo(minAmount) == 0) {
                return maxAmount;
            }
            BigDecimal range = maxAmount.subtract(minAmount);
            BigDecimal randomValue = range.multiply(BigDecimal.valueOf(random.nextDouble()));
            return minAmount.add(randomValue).setScale(2, RoundingMode.HALF_UP);
        } catch (Exception e) {
            logger.error("旧版红包规则计算异常", e);
            return BigDecimal.ZERO;
        }
    }
}
