package com.haha.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.common.constant.MarketingConstants;
import com.haha.common.enums.CouponStatusEnum;
import com.haha.common.enums.DistributeTypeEnum;
import com.haha.common.enums.TargetTypeEnum;
import com.haha.common.exception.BusinessException;
import com.haha.entity.CouponDistribute;
import com.haha.entity.CouponTemplate;
import com.haha.entity.User;
import com.haha.entity.UserCoupon;
import com.haha.entity.dto.CouponDistributeDTO;
import com.haha.mapper.CouponDistributeMapper;
import com.haha.mapper.CouponTemplateMapper;
import com.haha.mapper.UserCouponMapper;
import com.haha.mapper.UserMapper;
import com.haha.service.CouponTemplateService;
import com.haha.service.UserCouponService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements UserCouponService {

    private final UserCouponMapper userCouponMapper;
    private final CouponTemplateMapper templateMapper;
    private final CouponDistributeMapper distributeMapper;
    private final UserMapper userMapper;
    private final CouponTemplateService templateService;
    private final StringRedisTemplate redisTemplate;

    @Override
    public IPage<UserCoupon> getMyCoupons(Long userId, Integer status, int page, int pageSize) {
        Page<UserCoupon> pageParam = new Page<>(page, pageSize);

        LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserCoupon::getUserId, userId);
        if (status != null) {
            wrapper.eq(UserCoupon::getStatus, status);
        }
        wrapper.orderByDesc(UserCoupon::getCreateTime);

        IPage<UserCoupon> result = this.page(pageParam, wrapper);
        result.getRecords().forEach(this::fillCouponLabels);

        return result;
    }

    @Override
    public List<UserCoupon> getAvailableCoupons(Long userId) {
        LocalDateTime now = LocalDateTime.now();
        return userCouponMapper.selectByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getCode());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserCoupon receiveCoupon(Long userId, Long templateId) {
        String lockKey = MarketingConstants.REDIS_KEY_COUPON_RECEIVE_LOCK + userId + ":" + templateId;
        Boolean acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 1, TimeUnit.MINUTES);
        if (!acquired) {
            throw new BusinessException(400, "请勿重复领取");
        }

        try {
            CouponTemplate template = templateMapper.selectById(templateId);
            if (template == null || template.getDeleted() == 1) {
                throw new BusinessException(404, "优惠券不存在");
            }
            if (template.getStatus() != 1) {
                throw new BusinessException(400, "优惠券已禁用");
            }
            if (template.getRemainCount() <= 0) {
                throw new BusinessException(400, "优惠券已领完");
            }

            int receivedCount = userCouponMapper.countByTemplateAndUser(templateId, userId);
            if (receivedCount >= template.getReceiveLimit()) {
                throw new BusinessException(400, "已达到领取上限");
            }

            if (!templateService.decreaseRemainCount(templateId)) {
                throw new BusinessException(400, "优惠券领取失败，请重试");
            }

            UserCoupon userCoupon = new UserCoupon();
            userCoupon.setCouponCode(generateCouponCode());
            userCoupon.setTemplateId(templateId);
            userCoupon.setUserId(userId);
            userCoupon.setStatus(CouponStatusEnum.UNUSED.getCode());
            userCoupon.setReceiveTime(LocalDateTime.now());

            if (template.getValidType() == 1) {
                userCoupon.setValidStartTime(template.getValidStartTime());
                userCoupon.setValidEndTime(template.getValidEndTime());
            } else {
                LocalDateTime now = LocalDateTime.now();
                userCoupon.setValidStartTime(now);
                userCoupon.setValidEndTime(now.plusDays(template.getValidDays()));
            }

            this.save(userCoupon);

            log.info("用户领取优惠券成功: userId={}, templateId={}, couponCode={}", userId, templateId, userCoupon.getCouponCode());
            return userCoupon;

        } finally {
            redisTemplate.delete(lockKey);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void distributeCoupon(CouponDistributeDTO dto, Long operatorId, String operatorName) {
        CouponTemplate template = templateMapper.selectById(dto.getTemplateId());
        if (template == null || template.getDeleted() == 1) {
            throw new BusinessException(404, "优惠券模板不存在");
        }

        List<Long> targetUserIds = getTargetUserIds(dto.getTargetType(), dto.getUserIds());
        if (targetUserIds.isEmpty()) {
            throw new BusinessException(400, "没有目标用户");
        }

        if (targetUserIds.size() > template.getRemainCount()) {
            throw new BusinessException(400, "优惠券数量不足");
        }

        CouponDistribute distribute = new CouponDistribute();
        distribute.setTemplateId(dto.getTemplateId());
        distribute.setDistributeType(DistributeTypeEnum.TARGET_SEND.getCode());
        distribute.setTargetType(dto.getTargetType());
        distribute.setTargetCount(targetUserIds.size());
        distribute.setSuccessCount(0);
        distribute.setOperatorId(operatorId);
        distribute.setOperatorName(operatorName);

        distributeMapper.insert(distribute);

        int successCount = 0;
        for (Long userId : targetUserIds) {
            try {
                UserCoupon userCoupon = new UserCoupon();
                userCoupon.setCouponCode(generateCouponCode());
                userCoupon.setTemplateId(dto.getTemplateId());
                userCoupon.setUserId(userId);
                userCoupon.setStatus(CouponStatusEnum.UNUSED.getCode());
                userCoupon.setReceiveTime(LocalDateTime.now());

                if (template.getValidType() == 1) {
                    userCoupon.setValidStartTime(template.getValidStartTime());
                    userCoupon.setValidEndTime(template.getValidEndTime());
                } else {
                    LocalDateTime now = LocalDateTime.now();
                    userCoupon.setValidStartTime(now);
                    userCoupon.setValidEndTime(now.plusDays(template.getValidDays()));
                }

                this.save(userCoupon);
                templateService.decreaseRemainCount(dto.getTemplateId());
                successCount++;
            } catch (Exception e) {
                log.error("发放优惠券失败: userId={}, templateId={}", userId, dto.getTemplateId(), e);
            }
        }

        distribute.setSuccessCount(successCount);
        distributeMapper.updateById(distribute);

        log.info("批量发放优惠券完成: templateId={}, targetCount={}, successCount={}", dto.getTemplateId(), targetUserIds.size(), successCount);
    }

    @Override
    public UserCoupon getDetail(Long id, Long userId) {
        UserCoupon userCoupon = userCouponMapper.selectWithTemplate(id);
        if (userCoupon == null) {
            throw new BusinessException(404, "优惠券不存在");
        }
        if (!userCoupon.getUserId().equals(userId)) {
            throw new BusinessException(403, "无权访问该优惠券");
        }
        fillCouponLabels(userCoupon);
        return userCoupon;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean useCoupon(Long id, Long userId, Long orderId, BigDecimal discountAmount) {
        UserCoupon userCoupon = this.getById(id);
        if (userCoupon == null) {
            throw new BusinessException(404, "优惠券不存在");
        }
        if (!userCoupon.getUserId().equals(userId)) {
            throw new BusinessException(403, "无权使用该优惠券");
        }
        if (!CouponStatusEnum.canUse(userCoupon.getStatus())) {
            throw new BusinessException(400, "优惠券不可用");
        }
        if (LocalDateTime.now().isAfter(userCoupon.getValidEndTime())) {
            throw new BusinessException(400, "优惠券已过期");
        }

        userCoupon.setStatus(CouponStatusEnum.USED.getCode());
        userCoupon.setOrderId(orderId);
        userCoupon.setUseTime(LocalDateTime.now());
        userCoupon.setDiscountAmount(discountAmount);

        boolean result = this.updateById(userCoupon);

        if (result) {
            log.info("使用优惠券成功: id={}, userId={}, orderId={}, discountAmount={}", id, userId, orderId, discountAmount);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void expireCoupons() {
        int count = userCouponMapper.updateExpiredCoupons(LocalDateTime.now());
        if (count > 0) {
            log.info("更新过期优惠券完成: count={}", count);
        }
    }

    @Override
    public int countAvailableCoupons(Long userId) {
        return userCouponMapper.countAvailableByUserId(userId, LocalDateTime.now());
    }

    @Override
    public Map<String, Object> getCouponStatistics(Long templateId) {
        Map<String, Object> stats = new HashMap<>();
        stats.put("totalReceived", userCouponMapper.countByTemplateId(templateId));
        stats.put("totalUsed", userCouponMapper.countUsedByTemplateId(templateId));
        return stats;
    }

    private String generateCouponCode() {
        return MarketingConstants.COUPON_CODE_PREFIX + IdUtil.getSnowflakeNextIdStr();
    }

    private List<Long> getTargetUserIds(Integer targetType, List<Long> userIds) {
        if (targetType == TargetTypeEnum.SPECIFIED_USER.getCode()) {
            return userIds != null ? userIds : new ArrayList<>();
        } else if (targetType == TargetTypeEnum.ALL_USER.getCode()) {
            List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().select(User::getId));
            return users.stream().map(User::getId).toList();
        } else if (targetType == TargetTypeEnum.NEW_USER.getCode()) {
            LocalDateTime weekAgo = LocalDateTime.now().minusDays(7);
            List<User> users = userMapper.selectList(
                    new LambdaQueryWrapper<User>()
                            .select(User::getId)
                            .ge(User::getCreateTime, weekAgo));
            return users.stream().map(User::getId).toList();
        }
        return new ArrayList<>();
    }

    private void fillCouponLabels(UserCoupon userCoupon) {
        com.haha.common.vo.StatusLabel label = CouponStatusEnum.getLabelByCode(userCoupon.getStatus());
        userCoupon.setStatusLabel(label.getLabel());
        userCoupon.setStatusColor(label.getColor());
    }
}
