package com.starry.admin.modules.blindbox.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.blindbox.mapper.BlindBoxPoolMapper;
import com.starry.admin.modules.blindbox.module.constant.BlindBoxPoolStatus;
import com.starry.admin.modules.blindbox.module.dto.BlindBoxGiftOption;
import com.starry.admin.modules.blindbox.module.dto.BlindBoxPoolImportRow;
import com.starry.admin.modules.blindbox.module.dto.BlindBoxPoolUpsertRequest;
import com.starry.admin.modules.blindbox.module.dto.BlindBoxPoolView;
import com.starry.admin.modules.blindbox.module.entity.BlindBoxConfigEntity;
import com.starry.admin.modules.blindbox.module.entity.BlindBoxPoolEntity;
import com.starry.admin.modules.shop.mapper.PlayGiftInfoMapper;
import com.starry.admin.modules.shop.module.constant.GiftHistory;
import com.starry.admin.modules.shop.module.constant.GiftState;
import com.starry.admin.modules.shop.module.constant.GiftType;
import com.starry.admin.modules.shop.module.entity.PlayGiftInfoEntity;
import com.starry.admin.utils.SecurityUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
@RequiredArgsConstructor
public class BlindBoxPoolAdminService {

    private static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final BlindBoxPoolMapper blindBoxPoolMapper;
    private final BlindBoxConfigService blindBoxConfigService;
    private final PlayGiftInfoMapper playGiftInfoMapper;

    public List<BlindBoxPoolView> list(String blindBoxId) {
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlank(tenantId) || StrUtil.isBlank(blindBoxId)) {
            return Collections.emptyList();
        }
        BlindBoxConfigEntity config = blindBoxConfigService.requireById(blindBoxId);
        if (!tenantId.equals(config.getTenantId())) {
            throw new CustomException("盲盒不存在或已被移除");
        }
        LambdaQueryWrapper<BlindBoxPoolEntity> query = Wrappers.lambdaQuery(BlindBoxPoolEntity.class)
                .eq(BlindBoxPoolEntity::getTenantId, tenantId)
                .eq(BlindBoxPoolEntity::getBlindBoxId, blindBoxId)
                .eq(BlindBoxPoolEntity::getDeleted, Boolean.FALSE)
                .orderByAsc(BlindBoxPoolEntity::getId);
        List<BlindBoxPoolEntity> entities = blindBoxPoolMapper.selectList(query);
        if (CollUtil.isEmpty(entities)) {
            return Collections.emptyList();
        }
        Set<String> rewardIds = entities.stream()
                .map(BlindBoxPoolEntity::getRewardGiftId)
                .collect(Collectors.toSet());
        List<PlayGiftInfoEntity> gifts = playGiftInfoMapper.selectBatchIds(rewardIds);
        Map<String, PlayGiftInfoEntity> giftMap = gifts.stream()
                .collect(Collectors.toMap(PlayGiftInfoEntity::getId, Function.identity()));
        return entities.stream()
                .map(entity -> toView(entity, config, giftMap.get(entity.getRewardGiftId())))
                .collect(Collectors.toList());
    }

    public List<BlindBoxGiftOption> listGiftOptions(String keyword) {
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlank(tenantId)) {
            return Collections.emptyList();
        }
            LambdaQueryWrapper<PlayGiftInfoEntity> query = Wrappers.lambdaQuery(PlayGiftInfoEntity.class)
                    .eq(PlayGiftInfoEntity::getTenantId, tenantId)
                    .eq(PlayGiftInfoEntity::getHistory, GiftHistory.CURRENT.getCode())
                    .eq(PlayGiftInfoEntity::getState, GiftState.ACTIVE.getCode())
                    .eq(PlayGiftInfoEntity::getType, GiftType.NORMAL.getCode())
                    .eq(PlayGiftInfoEntity::getDeleted, Boolean.FALSE);
        if (StrUtil.isNotBlank(keyword)) {
            query.like(PlayGiftInfoEntity::getName, keyword.trim());
        }
        query.orderByAsc(PlayGiftInfoEntity::getName);
        List<PlayGiftInfoEntity> gifts = playGiftInfoMapper.selectList(query);
        if (CollUtil.isEmpty(gifts)) {
            return Collections.emptyList();
        }
        return gifts.stream()
                .map(gift -> new BlindBoxGiftOption(gift.getId(), gift.getName(), gift.getPrice(), gift.getUrl()))
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public BlindBoxPoolView create(String blindBoxId, BlindBoxPoolUpsertRequest request) {
        if (request == null) {
            throw new CustomException("参数不能为空");
        }
        String tenantId = requireTenantId();
        String targetBlindBoxId = StrUtil.isNotBlank(blindBoxId) ? blindBoxId : request.getBlindBoxId();
        if (StrUtil.isBlank(targetBlindBoxId)) {
            throw new CustomException("盲盒ID不能为空");
        }
        BlindBoxConfigEntity config = blindBoxConfigService.requireById(targetBlindBoxId);
        if (!tenantId.equals(config.getTenantId())) {
            throw new CustomException("盲盒不存在或已被移除");
        }
        PlayGiftInfoEntity rewardGift = requireRewardGift(tenantId, request.getRewardGiftId());
        validateTimeRange(request.getValidFrom(), request.getValidTo());
        Integer weight = requirePositiveWeight(request.getWeight(), rewardGift.getName());
        Integer remainingStock = normalizeRemainingStock(request.getRemainingStock(), rewardGift.getName());
        Integer status = resolveStatus(request.getStatus());
        BigDecimal rewardPrice = resolveRewardPrice(request.getRewardPrice(), rewardGift.getPrice());
        String operatorId = currentUserIdSafely();

        BlindBoxPoolEntity entity = new BlindBoxPoolEntity();
        entity.setTenantId(tenantId);
        entity.setBlindBoxId(targetBlindBoxId);
        entity.setRewardGiftId(rewardGift.getId());
        entity.setRewardPrice(rewardPrice);
        entity.setWeight(weight);
        entity.setRemainingStock(remainingStock);
        entity.setValidFrom(request.getValidFrom());
        entity.setValidTo(request.getValidTo());
        entity.setStatus(status);
        entity.setDeleted(Boolean.FALSE);
        entity.setCreatedBy(operatorId);
        entity.setUpdatedBy(operatorId);

        blindBoxPoolMapper.insert(entity);
        return toView(entity, config, rewardGift);
    }

    @Transactional(rollbackFor = Exception.class)
    public BlindBoxPoolView update(Long id, BlindBoxPoolUpsertRequest request) {
        if (id == null) {
            throw new CustomException("记录ID不能为空");
        }
        if (request == null) {
            throw new CustomException("参数不能为空");
        }
        String tenantId = requireTenantId();
        BlindBoxPoolEntity existing = blindBoxPoolMapper.selectById(id);
        if (existing == null || Boolean.TRUE.equals(existing.getDeleted())) {
            throw new CustomException("记录不存在或已删除");
        }
        if (!tenantId.equals(existing.getTenantId())) {
            throw new CustomException("无权操作该记录");
        }
        String targetBlindBoxId = StrUtil.isNotBlank(request.getBlindBoxId())
                ? request.getBlindBoxId()
                : existing.getBlindBoxId();
        BlindBoxConfigEntity config = blindBoxConfigService.requireById(targetBlindBoxId);
        if (!tenantId.equals(config.getTenantId())) {
            throw new CustomException("盲盒不存在或已被移除");
        }
        PlayGiftInfoEntity rewardGift = requireRewardGiftForUpdate(tenantId, request.getRewardGiftId());
        validateTimeRange(request.getValidFrom(), request.getValidTo());
        Integer weight = requirePositiveWeight(request.getWeight(), rewardGift.getName());
        Integer remainingStock = normalizeRemainingStock(request.getRemainingStock(), rewardGift.getName());
        Integer status = resolveStatus(request.getStatus());
        BigDecimal rewardPrice = resolveRewardPrice(request.getRewardPrice(), rewardGift.getPrice());
        String operatorId = currentUserIdSafely();

        existing.setBlindBoxId(targetBlindBoxId);
        existing.setRewardGiftId(rewardGift.getId());
        existing.setRewardPrice(rewardPrice);
        existing.setWeight(weight);
        existing.setRemainingStock(remainingStock);
        existing.setValidFrom(request.getValidFrom());
        existing.setValidTo(request.getValidTo());
        existing.setStatus(status);
        existing.setUpdatedBy(operatorId);
        blindBoxPoolMapper.updateById(existing);
        return toView(existing, config, rewardGift);
    }

    @Transactional(rollbackFor = Exception.class)
    public void replacePool(String blindBoxId, List<BlindBoxPoolImportRow> rows) {
        if (StrUtil.isBlank(blindBoxId)) {
            throw new CustomException("盲盒ID不能为空");
        }
        if (CollUtil.isEmpty(rows)) {
            throw new CustomException("导入数据不能为空");
        }
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlank(tenantId)) {
            throw new CustomException("租户信息缺失");
        }
        BlindBoxConfigEntity config = blindBoxConfigService.requireById(blindBoxId);
        if (!tenantId.equals(config.getTenantId())) {
            throw new CustomException("盲盒不存在或已被移除");
        }
        List<String> rewardNames = rows.stream()
                .map(BlindBoxPoolImportRow::getRewardGiftName)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(rewardNames)) {
            throw new CustomException("导入数据缺少中奖礼物名称");
        }
            LambdaQueryWrapper<PlayGiftInfoEntity> rewardQuery = Wrappers.lambdaQuery(PlayGiftInfoEntity.class)
                    .eq(PlayGiftInfoEntity::getTenantId, tenantId)
                    .eq(PlayGiftInfoEntity::getHistory, GiftHistory.CURRENT.getCode())
                    .eq(PlayGiftInfoEntity::getState, GiftState.ACTIVE.getCode())
                    .eq(PlayGiftInfoEntity::getType, GiftType.NORMAL.getCode())
                    .in(PlayGiftInfoEntity::getName, rewardNames);
        List<PlayGiftInfoEntity> rewardGifts = playGiftInfoMapper.selectList(rewardQuery);
        Map<String, List<PlayGiftInfoEntity>> rewardsByName = rewardGifts.stream()
                .collect(Collectors.groupingBy(PlayGiftInfoEntity::getName));
        List<String> duplicateNames = rewardsByName.entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(duplicateNames)) {
            throw new CustomException("存在同名礼物，无法区分：" + String.join("、", duplicateNames));
        }
        Map<String, PlayGiftInfoEntity> rewardMap = rewardsByName.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get(0)));
        String operatorId = currentUserIdSafely();
        List<BlindBoxPoolEntity> toInsert = new ArrayList<>(rows.size());
        for (BlindBoxPoolImportRow row : rows) {
            if (StrUtil.isBlank(row.getRewardGiftName())) {
                continue;
            }
            PlayGiftInfoEntity rewardGift = rewardMap.get(row.getRewardGiftName());
            if (rewardGift == null) {
                throw new CustomException("中奖礼物不存在: " + row.getRewardGiftName());
            }
            Integer weight = row.getWeight();
            if (weight == null || weight <= 0) {
                throw new CustomException("礼物 " + row.getRewardGiftName() + " 权重必须为正整数");
            }
            Integer remainingStock = row.getRemainingStock();
            if (remainingStock != null && remainingStock < 0) {
                throw new CustomException("礼物 " + row.getRewardGiftName() + " 库存不能为负数");
            }
                Integer status = row.getStatus() == null
                    ? BlindBoxPoolStatus.ENABLED.getCode()
                    : row.getStatus();
            BlindBoxPoolEntity entity = new BlindBoxPoolEntity();
            entity.setTenantId(tenantId);
            entity.setBlindBoxId(blindBoxId);
            entity.setRewardGiftId(rewardGift.getId());
            entity.setRewardPrice(resolveRewardPrice(row.getOverrideRewardPrice(), rewardGift.getPrice()));
            entity.setWeight(weight);
            entity.setRemainingStock(remainingStock);
            entity.setValidFrom(parseDateTime(row.getValidFrom()));
            entity.setValidTo(parseDateTime(row.getValidTo()));
            entity.setStatus(status);
            entity.setDeleted(Boolean.FALSE);
            entity.setCreatedBy(operatorId);
            entity.setUpdatedBy(operatorId);
            toInsert.add(entity);
        }
        if (CollUtil.isEmpty(toInsert)) {
            throw new CustomException("有效导入数据为空");
        }
        LambdaQueryWrapper<BlindBoxPoolEntity> deleteWrapper = Wrappers.lambdaQuery(BlindBoxPoolEntity.class)
                .eq(BlindBoxPoolEntity::getTenantId, tenantId)
                .eq(BlindBoxPoolEntity::getBlindBoxId, blindBoxId);
        blindBoxPoolMapper.delete(deleteWrapper);
        for (BlindBoxPoolEntity entity : toInsert) {
            blindBoxPoolMapper.insert(entity);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void removeById(Long id) {
        String tenantId = SecurityUtils.getTenantId();
        LambdaQueryWrapper<BlindBoxPoolEntity> wrapper = Wrappers.lambdaQuery(BlindBoxPoolEntity.class)
                .eq(BlindBoxPoolEntity::getTenantId, tenantId)
                .eq(BlindBoxPoolEntity::getId, id);
        int deleted = blindBoxPoolMapper.delete(wrapper);
        if (deleted == 0) {
            throw new CustomException("记录不存在或已删除");
        }
    }

    private BlindBoxPoolView toView(BlindBoxPoolEntity entity, BlindBoxConfigEntity config,
            PlayGiftInfoEntity rewardGift) {
        BlindBoxPoolView view = new BlindBoxPoolView();
        view.setId(entity.getId());
        view.setBlindBoxId(entity.getBlindBoxId());
        view.setBlindBoxName(config.getName());
        view.setRewardGiftId(entity.getRewardGiftId());
        view.setRewardGiftName(rewardGift != null ? rewardGift.getName() : entity.getRewardGiftId());
        view.setRewardPrice(entity.getRewardPrice());
        view.setWeight(entity.getWeight());
        view.setRemainingStock(entity.getRemainingStock());
        view.setValidFrom(entity.getValidFrom());
        view.setValidTo(entity.getValidTo());
        view.setStatus(entity.getStatus());
        return view;
    }

    private LocalDateTime parseDateTime(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        try {
            return LocalDateTime.parse(value.trim(), DATE_TIME_FORMATTER);
        } catch (DateTimeParseException ex) {
            throw new CustomException("日期格式应为 yyyy-MM-dd HH:mm:ss: " + value);
        }
    }

    private BigDecimal resolveRewardPrice(BigDecimal overrideRewardPrice, BigDecimal defaultPrice) {
        return overrideRewardPrice != null ? overrideRewardPrice : defaultPrice;
    }

    private String requireTenantId() {
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlank(tenantId)) {
            throw new CustomException("租户信息缺失");
        }
        return tenantId;
    }

    private PlayGiftInfoEntity requireRewardGift(String tenantId, String rewardGiftId) {
        return requireRewardGift(tenantId, rewardGiftId, true);
    }

    private PlayGiftInfoEntity requireRewardGiftForUpdate(String tenantId, String rewardGiftId) {
        return requireRewardGift(tenantId, rewardGiftId, false);
    }

    private PlayGiftInfoEntity requireRewardGift(String tenantId, String rewardGiftId, boolean strictAvailability) {
        if (StrUtil.isBlank(rewardGiftId)) {
            throw new CustomException("请选择中奖礼物");
        }
        PlayGiftInfoEntity gift = playGiftInfoMapper.selectById(rewardGiftId);
        if (gift == null
                || !tenantId.equals(gift.getTenantId())
                || !GiftType.NORMAL.getCode().equals(gift.getType())
                || Boolean.TRUE.equals(gift.getDeleted())) {
            throw new CustomException("中奖礼物不存在或已下架");
        }
        if (strictAvailability) {
            if (!GiftHistory.CURRENT.getCode().equals(gift.getHistory())
                    || !GiftState.ACTIVE.getCode().equals(gift.getState())) {
                throw new CustomException("中奖礼物不存在或已下架");
            }
        }
        return gift;
    }

    private Integer requirePositiveWeight(Integer weight, String giftName) {
        if (weight == null || weight <= 0) {
            String name = StrUtil.isBlank(giftName) ? "" : giftName;
            throw new CustomException(StrUtil.isBlank(name)
                    ? "奖池权重必须为正整数"
                    : "礼物 " + name + " 权重必须为正整数");
        }
        return weight;
    }

    private Integer normalizeRemainingStock(Integer remainingStock, String giftName) {
        if (remainingStock == null) {
            return null;
        }
        if (remainingStock < 0) {
            String name = StrUtil.isBlank(giftName) ? "" : giftName;
            throw new CustomException(StrUtil.isBlank(name)
                    ? "库存不能为负数"
                    : "礼物 " + name + " 库存不能为负数");
        }
        return remainingStock;
    }

    private Integer resolveStatus(Integer status) {
        if (status == null) {
            return BlindBoxPoolStatus.ENABLED.getCode();
        }
        if (status.equals(BlindBoxPoolStatus.ENABLED.getCode())
                || status.equals(BlindBoxPoolStatus.DISABLED.getCode())) {
            return status;
        }
        throw new CustomException("状态参数非法");
    }

    private void validateTimeRange(LocalDateTime validFrom, LocalDateTime validTo) {
        if (validFrom != null && validTo != null && validFrom.isAfter(validTo)) {
            throw new CustomException("生效时间不能晚于失效时间");
        }
    }

    private String currentUserIdSafely() {
        try {
            return SecurityUtils.getUserId();
        } catch (RuntimeException ex) {
            return null;
        }
    }
}
