package com.fs.live.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.fs.common.constant.LiveKeysConstant;
import com.fs.common.core.domain.R;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.core.redis.RedisUtil;
import com.fs.common.utils.DateUtils;
import com.fs.live.domain.*;
import com.fs.live.enums.FsUserIntegralLogTypeEnum;
import com.fs.live.mapper.LiveMapper;
import com.fs.live.mapper.LiveRedConfMapper;
import com.fs.live.mapper.LiveRewardRecordMapper;
import com.fs.live.mapper.LiveUserRedRecordMapper;
import com.fs.live.param.RedPO;
import com.fs.live.service.ILiveRedConfService;
import com.fs.live.domain.LiveAutoTask;
import com.fs.live.service.ILiveAutoTaskService;
import cn.hutool.json.JSONObject;
import com.fs.store.domain.FsUser;
import com.fs.store.service.IFsUserService;
import com.fs.store.service.impl.FsUserServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 直播红包记录配置Service业务层处理
 *
 * @author fs
 * @date 2025-07-17
 */
@Service
public class LiveRedConfServiceImpl implements ILiveRedConfService {

    private static final Logger log = LoggerFactory.getLogger(LiveRedConfServiceImpl.class);
    @Autowired
    private LiveUserRedRecordMapper userRedRecordMapper;

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IFsUserService userService;
    @Autowired
    private LiveMapper liveMapper;
    @Autowired
    private LiveRewardRecordMapper liveRewardRecordMapper;

    private static final String REDPACKET_REMAININGLOTS_KEY = "live:red:remainingLots:";
    private static final String REDPACKET_REMAININGNUM_KEY = "live:red:remainingNum:";
    private static final String REDPACKET_CLAIM_KEY = "live:red:claim:";

    @Autowired
    private LiveRedConfMapper baseMapper;
    @Autowired
    private FsUserServiceImpl fsUserServiceImpl;
    @Autowired
    private ILiveAutoTaskService liveAutoTaskService;

    /**
     * 查询直播红包记录配置
     *
     * @param redId 直播红包记录配置主键
     * @return 直播红包记录配置
     */
    @Override
    public LiveRedConf selectLiveRedConfByRedId(Long redId)
    {
        return baseMapper.selectLiveRedConfByRedId(redId);
    }

    /**
     * 查询直播红包记录配置列表
     *
     * @param liveRedConf 直播红包记录配置
     * @return 直播红包记录配置
     */
    @Override
    public List<LiveRedConf> selectLiveRedConfList(LiveRedConf liveRedConf)
    {
        return baseMapper.selectLiveRedConfList(liveRedConf);
    }

    /**
     * 新增直播红包记录配置
     *
     * @param liveRedConf 直播红包记录配置
     * @return 结果
     */
    @Override
    public int insertLiveRedConf(LiveRedConf liveRedConf)
    {
        liveRedConf.setCreateTime(DateUtils.getNowDate());
        return baseMapper.insertLiveRedConf(liveRedConf);
    }

    /**
     * 修改直播红包记录配置
     *
     * @param liveRedConf 直播红包记录配置
     * @return 结果
     */
    @Override
    public int updateLiveRedConf(LiveRedConf liveRedConf)
    {
        String cacheKey = "live:red_task:" + liveRedConf.getLiveId();
        liveRedConf.setUpdateTime(DateUtils.getNowDate());
        // 开始
        if (liveRedConf.getRedStatus() == 1) {
            setRemaining(liveRedConf.getRedId(), liveRedConf.getTotalLots());
            LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(liveRedConf.getDuration());
            double score = localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
            redisCache.redisTemplate.opsForZSet().add(cacheKey, String.valueOf(liveRedConf.getRedId()), score);
            redisCache.redisTemplate.expire(cacheKey, 30, TimeUnit.MINUTES);
        } else {
            // 其他
            redisCache.deleteObject(REDPACKET_REMAININGLOTS_KEY + liveRedConf.getRedId());
            redisCache.deleteObject(cacheKey);
            redStatusUpdate(CollUtil.newHashSet(liveRedConf.getRedId()));
        }
        return baseMapper.updateLiveRedConf(liveRedConf);
    }

    /**
     * 批量删除直播红包记录配置
     *
     * @param redIds 需要删除的直播红包记录配置主键
     * @return 结果
     */
    @Override
    public int deleteLiveRedConfByRedIds(Long[] redIds)
    {
        // 删除前检查live_auto_task，删除task_type=2的定时任务
        for (Long redId : redIds) {
            LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(redId);
            if (conf != null && conf.getLiveId() != null) {
                // 删除相关的自动化任务
                liveAutoTaskService.deleteAutoTasksByRedId(redId, conf.getLiveId());
                // 删除红包缓存
                liveAutoTaskService
                        .deleteRedCache(redId, conf.getLiveId());
            }
        }
        return baseMapper.deleteLiveRedConfByRedIds(redIds);
    }

    /**
     * 删除红包相关的定时任务（task_type=2）
     */
    private void deleteRelatedAutoTasksForRed(Long redId) {
        LiveAutoTask queryTask = new LiveAutoTask();
        List<LiveAutoTask> tasks = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
        List<Long> taskIdsToDelete = new ArrayList<>();

        for (LiveAutoTask task : tasks) {
            if (task.getTaskType() != null && task.getTaskType() == 2L) {
                if (task.getContent() != null && !task.getContent().isEmpty()) {
                    try {
                        JSONObject contentJson = JSONUtil.parseObj(task.getContent());
                        Object contentRedId = contentJson.get("redId");
                        if (contentRedId != null && redId.equals(Long.parseLong(contentRedId.toString()))) {
                            taskIdsToDelete.add(task.getId());
                        }
                    } catch (Exception e) {
                        // 解析失败，跳过
                    }
                }
            }
        }

        if (!taskIdsToDelete.isEmpty()) {
            liveAutoTaskService.deleteLiveAutoTaskByIds(taskIdsToDelete.toArray(new Long[0]));
        }
    }

    /**
     * 删除直播红包记录配置信息
     *
     * @param redId 直播红包记录配置主键
     * @return 结果
     */
    @Override
    public int deleteLiveRedConfByRedId(Long redId)
    {
        // 先查询红包配置，获取liveId
        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(redId);
        if (conf != null && conf.getLiveId() != null) {
            // 删除相关的自动化任务
            liveAutoTaskService
                    .deleteAutoTasksByRedId(redId, conf.getLiveId());
            // 删除红包缓存
            liveAutoTaskService
                    .deleteRedCache(redId, conf.getLiveId());
        }
        return baseMapper.deleteLiveRedConfByRedId(redId);
    }

    @Override
    @Transactional
    public LiveRedConf getById(Long redId) {
        return baseMapper.selectById(redId);
    }

    @Override
    @Transactional
    public List<LiveRedConf> getByLiveId(Long liveId) {
        return baseMapper.selectByLiveId(liveId);
    }

    @Override
    @Transactional
    public void create(LiveRedConf conf) {
        baseMapper.insert(conf);
    }

    @Override
    @Transactional
    public void update(LiveRedConf conf) {
        baseMapper.update(conf);
    }

    @Override
    @Transactional
    public void delete(Long redId) {
        // 先查询红包配置，获取liveId
        LiveRedConf conf = baseMapper.selectById(redId);
        if (conf != null && conf.getLiveId() != null) {
            // 删除相关的自动化任务
            liveAutoTaskService
                    .deleteAutoTasksByRedId(redId, conf.getLiveId());
            // 删除红包缓存
            liveAutoTaskService
                    .deleteRedCache(redId, conf.getLiveId());
        }
        baseMapper.deleteById(redId);
    }

    /**
     * 用户领取红包
     */
    @Override
    @Transactional
    public R claimRedPacket(RedPO red) {
        // String claimKey = REDPACKET_CLAIM_KEY + red.getRedId();
        Object o = redisUtil.hashGet(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()));
        if (ObjectUtil.isNotEmpty(o)) {
            return R.error("您已经领取过红包了！");
        }
        /*try {*/
/*            //获取红包锁
            if (!tryLock(claimKey, red.getUserId().toString(), 5)) {
                return R.error("您已经领取过红包了！");
            }*/

        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(red.getRedId());
        if (conf == null || conf.getRedStatus() != 1) {
            return R.error("手慢了，红包已结束~");
        }
        //redis剩余红包数
        // 平均分 暂时不适用redis 记录红包数
        Long integral = calculateIntegralAverage(conf);
        if (0L == integral) {
            return R.error("手慢了，红包被抢完了~");
        }

        // 更新数据库
/*
        Date now = new Date();
        conf.setTotalSend(conf.getTotalSend() + 1);
        conf.setRemaining(Math.toIntExact(conf.getTotalLots() - conf.getTotalSend()));
        conf.setUpdateTime(now);
        baseMapper.updateLiveRedConf(conf);
*/

        // 最后更新缓存
        if (getRemaining(red.getRedId()) <= 0 || !decreaseRemainingLotsIfPossible(red.getRedId())) {
            LiveRedConf liveRedConf = new LiveRedConf();
            liveRedConf.setRedId(red.getRedId());
            liveRedConf.setRedStatus(2L);
            baseMapper.updateLiveRedConf(liveRedConf);
            Set<String> range = CollUtil.newHashSet(String.valueOf(red.getRedId()));
            finishRedStatusBySetIds(range);
            return R.error("手慢了，红包已被抢完~");
        }
        // 记录用户红包
        LiveUserRedRecord record = new LiveUserRedRecord();
        record.setRedId(red.getRedId());
        record.setLiveId(red.getLiveId());
        record.setUserId(red.getUserId());
        record.setIntegral(integral);
        record.setCreateTime(new Date());
        // WebSocket 通知
        //String msg = String.format("用户 %d 抢到了红包 %d，获得 %d 芳华币", userId, redId, integral);
        //WebSocketServer.notifyUsers(msg);
        redisUtil.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
        FsUser fsUser = new FsUser();
        fsUser.setUserId(red.getUserId());
        fsUser.setIntegral(new BigDecimal(integral));
        fsUserServiceImpl.openPlatformGeneralUserIntegralHandle(Collections.singletonList(fsUser),red.getRedId(), 0L, FsUserIntegralLogTypeEnum.TYPE_29.getValue());
        return R.ok("恭喜您成功抢到" + integral + "芳华币");
/*        } catch (Exception e) {
            e.printStackTrace();
            log.error("抢红包异常：" + e.getMessage());
        }*/
        // return R.error("抢红包异常");
    }

    @Override
    public String start(String redId, Long userId) {
        LiveRedConf conf = baseMapper.selectLiveRedConfByRedId(Long.valueOf(redId));
        if (conf != null && conf.getRedStatus() == 0) {
            conf.setRedStatus(1L);
            conf.setUpdateTime(new Date());
            conf.setUpdateBy(userId.toString());
            baseMapper.update(conf);
            return "红包发放成功";
        }
        return "红包发放失败";
    }

    @Override
    public List<LiveRedConf> selectByLiveId(Long existLiveId) {
        return baseMapper.selectByLiveId(existLiveId);
    }

    @Override
    public List<LiveRedConf> selectActivedRed(Long liveId) {
        return baseMapper.selectActivedRed(liveId);
    }

    @Override
    public void finishRedStatusBySetIds(Set<String> range) {
        try {
            log.info("开始结束红包状态:{}",range);
            baseMapper.finishRedStatusBySetIds(range);
            redStatusUpdate(range.stream().map(Long::valueOf).collect(Collectors.toSet()));
            log.info("结束红包状态完成");
        }catch (Exception e){
            log.info("红包状态结束异常",e);
        }

    }

    @Override
    public void redStatusUpdate(Set<Long> redIds) {
        // 插入抽奖记录
        for (Long id : redIds) {
            LiveRedConf liveRedConf = baseMapper.selectLiveRedConfByRedId(id);
            // 更新数据库
            updateDbByRed(liveRedConf);
            String hashKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, liveRedConf.getLiveId(), liveRedConf.getRedId());
            Map<Object, Object> hashEntries = redisUtil.hashEntries(hashKey);
            List<LiveUserRedRecord> liveUserRedRecords = new ArrayList<>();
            if (CollUtil.isNotEmpty(hashEntries)) {
                liveUserRedRecords = hashEntries.values().stream()
                        .map(value -> JSONUtil.toBean(JSONUtil.parseObj(value), LiveUserRedRecord.class))
                        .collect(Collectors.toList());
                userRedRecordMapper.insertLiveUserRedRecordBatch(liveUserRedRecords);
                for (LiveUserRedRecord liveUserRedRecord : liveUserRedRecords) {
                    userService.incrIntegral(Collections.singletonList(liveUserRedRecord.getUserId()), liveUserRedRecord.getIntegral());
                    // 保存用户领取芳华币记录 方便统计计算
                    saveUserRewardRecord(liveUserRedRecord);
                }
            }
            redisUtil.delete(hashKey);
        }
    }

    private void saveUserRewardRecord(LiveUserRedRecord liveUserRedRecord) {
        Live live = liveMapper.selectLiveByLiveId(liveUserRedRecord.getLiveId());
        LiveRewardRecord record = new LiveRewardRecord();
        record.setLiveId(live.getLiveId());
        record.setUserId(liveUserRedRecord.getUserId());
        record.setIncomeType(1L);
        record.setSourceType(2L);
        record.setSourceId(live.getCompanyId() == null ? 0L : live.getCompanyId());
        record.setRewardType(2L);
        record.setNum(BigDecimal.valueOf(liveUserRedRecord.getIntegral()));
        record.setRewardType(2L);
        record.setCreateTime(new Date());
        record.setCreateBy(String.valueOf(liveUserRedRecord.getUserId()));
        liveRewardRecordMapper.insertLiveRewardRecord(record);
    }

    @Override
    public void updateRedQuantityNum() {
        LiveRedConf redConf = new LiveRedConf();
        redConf.setRedStatus(1L);
        List<LiveRedConf> liveRedConfs = baseMapper.selectLiveRedConfList(redConf);
        for (LiveRedConf liveRedConf : liveRedConfs) {
            updateDbByRed(liveRedConf);
        }
    }

    @Override
    public List<LiveRedConf> selectLiveRedConfListOn(LiveRedConf liveRedConf) {
        return baseMapper.selectLiveRedConfListOn(liveRedConf);
    }

    private void updateDbByRed(LiveRedConf red) {
        // 更新数据库
        Date now = new Date();
        Integer remaining = getRemaining(red.getRedId());
        red.setTotalSend(redisUtil.size(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId())));
        red.setRemaining(remaining);
        red.setUpdateTime(now);
        baseMapper.updateLiveRedConf(red);
//        log.info("更新红包领取数量完成 {} {}",red.getRedId(),remaining);
    }

    // 初始化剩余数量
    public void initRemainingLots(Long redId, Integer totalLots) {
        String key = REDPACKET_REMAININGLOTS_KEY + redId;
        redisCache.setCacheObject(key, totalLots.toString());
    }
    // 初始化剩余金额
    public void initRemainingNum(Long redId, Long redNum) {
        String key = REDPACKET_REMAININGNUM_KEY + redId;
        redisCache.setCacheObject(key, redNum.toString());
    }

    // 获取剩余红包数量
    public Integer getRemaining(Long redId) {
        String key = REDPACKET_REMAININGLOTS_KEY + redId;
        Integer value = redisCache.getCacheObject(key);
        return value == null ? 0 : value;
    }
    // 获取剩余红包数量
    public void setRemaining(Long redId,Long totalLots) {
        String key = REDPACKET_REMAININGLOTS_KEY + redId;
        redisCache.setCacheObject(key,totalLots.intValue(),5,TimeUnit.MINUTES);
    }
    // 获取剩余红包金额数量
    public Integer getRemainingNum(Long redId) {
        String key = REDPACKET_REMAININGNUM_KEY + redId;
        String value = redisCache.getCacheObject(key);
        return value == null ? 0 : Integer.parseInt(value);
    }

    // 减少剩余数量（原子操作）
    public boolean decreaseRemainingLotsIfPossible(Long redId) {
        String key = REDPACKET_REMAININGLOTS_KEY + redId;
        // 通过lua脚本校验剩余数量是否大于0
        String script = "local current = redis.call('GET', KEYS[1]) " +
                "if current and tonumber(current) > 0 then " +
                "   return redis.call('DECRBY', KEYS[1], 1) " +
                "else " +
                "   return -1 " +
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        Object result = redisCache.redisTemplate.execute(redisScript, Collections.singletonList(key));
        return result != null && Long.valueOf(result.toString()) >= 0;
    }
    // 减少剩余数量（原子操作）
    public void decreaseRemainingNumIfPossible(Long redId, Long integral) {
        String key = REDPACKET_REMAININGNUM_KEY + redId;
        redisCache.incrementCacheValue(key, -integral);
    }

    public boolean tryLock(String lockKey, String clientId, long expireTime) {
        Boolean success = redisCache.setIfAbsent(lockKey + ":" + clientId, clientId, expireTime, TimeUnit.MINUTES);
        return Boolean.TRUE.equals(success);
    }

    public void releaseLock(List<String> lockKey, String clientId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV then return redis.call('del', KEYS[1]) else return 0 end";
        redisCache.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), lockKey, clientId);
    }

    private Long calculateIntegralRandom(LiveRedConf conf) {
        if (conf.getTotalSend() >= conf.getTotalLots()) {
            return 0L; // 已发完
        }
        //剩余红包个数
        long remainingLots = conf.getTotalLots() - conf.getTotalSend();
        //剩余红包金额
        int remainingNum = getRemainingNum(conf.getRedId());
        if (remainingLots == 1){
            return (long) remainingNum;
        }
        long avgIntegral = remainingNum / remainingLots;
        long minIntegral = avgIntegral / 3 == 0 ? 1 : avgIntegral / 3;
        long maxIntegral = avgIntegral * 3 >= remainingNum ? remainingNum : avgIntegral * 3;
        return ThreadLocalRandom.current().nextLong(minIntegral, maxIntegral + 1);
    }

    private Long calculateIntegralAverage(LiveRedConf conf) {
        if (conf.getTotalSend() >= conf.getTotalLots()) {
            return 0L; // 已发完
        }
        return conf.getRedNum() / conf.getTotalLots();
    }
}
