package com.gxwebsoft.shop.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopSeckill;
import com.gxwebsoft.shop.entity.ShopSeckillOrder;
import com.gxwebsoft.shop.mapper.ShopSeckillMapper;
import com.gxwebsoft.shop.mapper.ShopSeckillOrderMapper;
import com.gxwebsoft.shop.param.ShopSeckillParam;
import com.gxwebsoft.shop.service.ShopGoodsService;
import com.gxwebsoft.shop.service.ShopSeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 秒杀活动Service实现
 *
 * @author 科技小王子
 * @since 2026-06-25
 */
@Slf4j
@Service
public class ShopSeckillServiceImpl extends ServiceImpl<ShopSeckillMapper, ShopSeckill> implements ShopSeckillService {

    @Resource
    private ShopSeckillOrderMapper seckillOrderMapper;

    @Resource
    private ShopGoodsService shopGoodsService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /** Redis key 前缀 */
    private static final String SECKILL_STOCK_KEY = "seckill:stock:";
    private static final String SECKILL_ORDER_KEY = "seckill:order:";
    private static final String SECKILL_LOCK_KEY = "seckill:lock:";

    @Override
    public PageResult<ShopSeckill> pageRel(ShopSeckillParam param) {
        PageParam<ShopSeckill, ShopSeckillParam> page = new PageParam<>(param);
        page.setDefaultOrder("sort_number asc, start_time asc");
        List<ShopSeckill> list = baseMapper.selectPageRel(page, param);
        // 填充关联商品完整信息
        fillProductInfo(list);
        // 自动更新过期状态
        updateStatusByTime(list);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<ShopSeckill> listRel(ShopSeckillParam param) {
        List<ShopSeckill> list = baseMapper.selectListRel(param);
        fillProductInfo(list);
        updateStatusByTime(list);
        return list;
    }

    @Override
    public ShopSeckill getByIdRel(Integer seckillId) {
        ShopSeckillParam param = new ShopSeckillParam();
        param.setSeckillId(seckillId);
        List<ShopSeckill> list = baseMapper.selectListRel(param);
        ShopSeckill seckill = param.getOne(list);
        if (seckill != null) {
            // 加载完整商品信息
            fillSingleProductInfo(seckill);
            // 自动更新过期状态
            updateSingleStatusByTime(seckill);
        }
        return seckill;
    }

    /**
     * 创建秒杀订单 —— 核心业务逻辑
     *
     * 流程：
     * 1. 防重复提交（Redis 分布式锁）
     * 2. 活动校验（状态、时间）
     * 3. 限购校验（同一用户购买次数）
     * 4. 原子扣减库存（乐观锁 SQL）
     * 5. 创建秒杀订单记录
     * 6. 生成正式订单号（后续关联支付）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ShopSeckillOrder createSeckillOrder(Integer seckillId, Integer goodsId, Integer skuId,
                                                 Integer quantity, Integer userId, Integer tenantId) {
        log.info("[秒杀] 用户{}尝试秒杀活动{}，商品{}，数量{}", userId, seckillId, goodsId, quantity);

        // ======== 1. 防重复提交（同一用户5秒内不能重复下单）========
        String lockKey = SECKILL_LOCK_KEY + tenantId + ":" + seckillId + ":" + userId;
        Boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS);
        if (locked == null || !locked) {
            log.warn("[秒杀] 用户{}重复提交秒杀请求，活动{}", userId, seckillId);
            throw new BusinessException("请勿重复提交，5秒后可重试");
        }

        try {
            // ======== 2. 活动校验 ========
            ShopSeckill seckill = baseMapper.selectById(seckillId);
            if (seckill == null) {
                throw new BusinessException("秒杀活动不存在");
            }

            // 时间校验
            LocalDateTime now = LocalDateTime.now();
            if (now.isBefore(seckill.getStartTime())) {
                throw new BusinessException("活动尚未开始，开始时间：" + seckill.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }
            if (now.isAfter(seckill.getEndTime())) {
                throw new BusinessException("活动已结束");
            }

            // 状态校验
            if (seckill.getStatus() != null && seckill.getStatus() == 2) {
                throw new BusinessException("活动已结束");
            }

            // 库存校验（先粗判，后面原子扣减再做精确判断）
            if (seckill.getSeckillStock() == null || seckill.getSeckillStock() <= 0) {
                throw new BusinessException("已抢光，下次再来");
            }

            // 数量校验
            if (quantity == null || quantity <= 0) {
                quantity = 1;
            }

            // ======== 3. 限购校验 ========
            if (seckill.getLimitPerUser() != null && seckill.getLimitPerUser() > 0) {
                LambdaQueryWrapper<ShopSeckillOrder> countWrapper = new LambdaQueryWrapper<>();
                countWrapper.eq(ShopSeckillOrder::getSeckillId, seckillId)
                        .eq(ShopSeckillOrder::getUserId, userId)
                        .ne(ShopSeckillOrder::getOrderStatus, 2); // 排除已取消的订单
                long boughtCount = seckillOrderMapper.selectCount(countWrapper);
                if (boughtCount + quantity > seckill.getLimitPerUser()) {
                    throw new BusinessException("每人限购" + seckill.getLimitPerUser() + "件，您已购买" + boughtCount + "件");
                }
            }

            // ======== 4. 原子扣减库存（乐观锁）========
            int affected = baseMapper.deductSeckillStock(seckillId, quantity);
            if (affected <= 0) {
                log.warn("[秒杀] 库存扣减失败，活动{}可能已售罄", seckillId);
                throw new BusinessException("已抢光，下次再来");
            }

            // ======== 5. 创建秒杀订单 ========
            ShopSeckillOrder order = new ShopSeckillOrder();
            order.setSeckillId(seckillId);
            order.setGoodsId(goodsId);
            order.setSkuId(skuId != null ? skuId : 0);
            order.setQuantity(quantity);
            order.setSeckillPrice(seckill.getSeckillPrice());
            order.setUserId(userId);
            order.setTenantId(tenantId);
            order.setOrderNo("SK" + IdUtil.getSnowflakeNextIdStr());
            order.setOrderStatus(0); // 待支付
            order.setCreateTime(LocalDateTime.now());
            order.setUpdateTime(LocalDateTime.now());

            seckillOrderMapper.insert(order);

            // ======== 6. 记录用户已购（Redis Set，加速限购校验）========
            String orderKey = SECKILL_ORDER_KEY + tenantId + ":" + seckillId + ":" + userId;
            redisUtil.sAdd(orderKey, String.valueOf(order.getSeckillOrderId()));
            // 设置过期时间为活动结束时间之后24小时
            long expireSeconds = seckill.getEndTime().plusHours(24).atZone(java.time.ZoneId.systemDefault())
                    .toEpochSecond() - System.currentTimeMillis() / 1000;
            if (expireSeconds > 0) {
                stringRedisTemplate.expire(orderKey, expireSeconds, TimeUnit.SECONDS);
            }

            // 更新秒杀活动状态为进行中
            seckill.setStatus(1);
            baseMapper.updateById(seckill);

            log.info("[秒杀] 用户{}秒杀成功！订单号：{}", userId, order.getOrderNo());
            return order;

        } finally {
            // 释放分布式锁（即使异常也要释放，因为锁有5秒自动过期）
            // 这里不主动释放，依赖5秒自动过期，防止事务还没提交就释放导致并发问题
        }
    }

    @Override
    public PageResult<ShopSeckillOrder> mySeckillOrders(Integer userId, Integer page, Integer pageSize) {
        if (page == null || page <= 0) page = 1;
        if (pageSize == null || pageSize <= 0) pageSize = 10;

        LambdaQueryWrapper<ShopSeckillOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ShopSeckillOrder::getUserId, userId)
                .orderByDesc(ShopSeckillOrder::getCreateTime);

        com.baomidou.mybatisplus.extension.plugins.pagination.Page<ShopSeckillOrder> mpPage =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, pageSize);
        seckillOrderMapper.selectPage(mpPage, wrapper);

        return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
    }

    @Override
    public List<Map<String, Object>> getSeckillTimeSlots() {
        // 查询所有未结束的秒杀活动
        LambdaQueryWrapper<ShopSeckill> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(ShopSeckill::getStatus, 2)
                .orderByAsc(ShopSeckill::getStartTime);
        List<ShopSeckill> list = baseMapper.selectList(wrapper);
        updateStatusByTime(list);

        // 按时间段分组
        DateTimeFormatter hourFmt = DateTimeFormatter.ofPattern("HH:mm");
        Map<String, Map<String, Object>> slots = new LinkedHashMap<>();

        for (ShopSeckill s : list) {
            String slotKey = s.getStartTime().format(hourFmt) + "-" + s.getEndTime().format(hourFmt);
            if (!slots.containsKey(slotKey)) {
                Map<String, Object> slot = new HashMap<>();
                slot.put("startTime", s.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                slot.put("endTime", s.getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                // 判断场次状态：全未开始=0，有进行中=1，全结束=2
                slot.put("status", s.getStatus());
                slot.put("seckillList", new ArrayList<ShopSeckill>());
                slots.put(slotKey, slot);
            }
            ((List<ShopSeckill>) slots.get(slotKey).get("seckillList")).add(s);
        }

        // 最终状态判断
        for (Map<String, Object> slot : slots.values()) {
            List<ShopSeckill> seckillList = (List<ShopSeckill>) slot.get("seckillList");
            boolean hasActive = seckillList.stream().anyMatch(s -> s.getStatus() == 1);
            boolean allEnded = seckillList.stream().allMatch(s -> s.getStatus() == 2);
            slot.put("status", allEnded ? 2 : hasActive ? 1 : 0);
        }

        return new ArrayList<>(slots.values());
    }

    @Override
    public void updateExpiredSeckillStatus() {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<ShopSeckill> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ShopSeckill::getStatus, 1)
                .lt(ShopSeckill::getEndTime, now);
        List<ShopSeckill> expiredList = baseMapper.selectList(wrapper);
        for (ShopSeckill s : expiredList) {
            s.setStatus(2);
            baseMapper.updateById(s);
            log.info("[秒杀] 自动更新过期活动：{} → 已结束", s.getSeckillId());
        }

        // 同时把到达开始时间的未开始活动标记为进行中
        LambdaQueryWrapper<ShopSeckill> startWrapper = new LambdaQueryWrapper<>();
        startWrapper.eq(ShopSeckill::getStatus, 0)
                .le(ShopSeckill::getStartTime, now)
                .gt(ShopSeckill::getEndTime, now);
        List<ShopSeckill> startingList = baseMapper.selectList(startWrapper);
        for (ShopSeckill s : startingList) {
            s.setStatus(1);
            baseMapper.updateById(s);
            log.info("[秒杀] 自动更新开始活动：{} → 进行中", s.getSeckillId());
        }
    }

    // ========== 私有辅助方法 ==========

    /** 填充关联商品完整信息 */
    private void fillProductInfo(List<ShopSeckill> list) {
        if (list == null || list.isEmpty()) return;
        List<Integer> goodsIds = list.stream()
                .map(ShopSeckill::getGoodsId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (goodsIds.isEmpty()) return;

        List<ShopGoods> goodsList = shopGoodsService.listByIds(goodsIds);
        Map<Integer, ShopGoods> goodsMap = goodsList.stream()
                .collect(Collectors.toMap(ShopGoods::getGoodsId, g -> g, (a, b) -> a));

        for (ShopSeckill s : list) {
            if (s.getGoodsId() != null) {
                ShopGoods goods = goodsMap.get(s.getGoodsId());
                if (goods != null) {
                    s.setProduct(goods);
                    // 如果关联查询没拿到名称/图片，从商品信息补充
                    if (s.getGoodsName() == null) s.setGoodsName(goods.getName());
                    if (s.getGoodsImage() == null) s.setGoodsImage(goods.getImage());
                    if (s.getOriginalPrice() == null) s.setOriginalPrice(goods.getPrice());
                }
            }
        }
    }

    /** 填充单个商品的完整信息 */
    private void fillSingleProductInfo(ShopSeckill seckill) {
        if (seckill == null || seckill.getGoodsId() == null) return;
        ShopGoods goods = shopGoodsService.getByIdRel(seckill.getGoodsId());
        if (goods != null) {
            seckill.setProduct(goods);
            if (seckill.getGoodsName() == null) seckill.setGoodsName(goods.getName());
            if (seckill.getGoodsImage() == null) seckill.setGoodsImage(goods.getImage());
            if (seckill.getOriginalPrice() == null) seckill.setOriginalPrice(goods.getPrice());
        }
    }

    /** 根据当前时间自动更新秒杀状态 */
    private void updateStatusByTime(List<ShopSeckill> list) {
        LocalDateTime now = LocalDateTime.now();
        for (ShopSeckill s : list) {
            updateSingleStatusByTime(s, now);
        }
    }

    private void updateSingleStatusByTime(ShopSeckill s) {
        updateSingleStatusByTime(s, LocalDateTime.now());
    }

    private void updateSingleStatusByTime(ShopSeckill s, LocalDateTime now) {
        if (s == null || s.getStartTime() == null || s.getEndTime() == null) return;

        if (now.isBefore(s.getStartTime())) {
            s.setStatus(0); // 未开始
        } else if (now.isAfter(s.getEndTime())) {
            s.setStatus(2); // 已结束
        } else {
            s.setStatus(1); // 进行中
        }
    }

}
