package com.jeesharp.business.modules.coupon.application.support;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jeesharp.business.modules.coupon.dto.CouponCheckoutContextDTO;
import com.jeesharp.business.modules.coupon.entity.CouponTemplate;
import com.jeesharp.business.modules.coupon.entity.MemberCoupon;
import com.jeesharp.business.modules.coupon.enums.CouponCheckoutUnusableReasonEnum;
import com.jeesharp.business.modules.coupon.enums.CouponUseScopeEnum;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 优惠券场景、渠道、等级与使用范围匹配规则（纯判断，不写库）。
 *
 * @author jeesharp
 * @since 2026-06-05
 */
@Component
public class CouponMatchSupport {

    /**
     * 按会员券快照（优先）与模板兜底规则判断是否不可用；返回 null 表示规则维度通过。
     */
    public CouponCheckoutUnusableReasonEnum matchMemberCouponRule(
            MemberCoupon coupon, CouponTemplate template, CouponCheckoutContextDTO context) {
        if (coupon == null || context == null) {
            return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
        }
        CouponTemplate effective = buildEffectiveTemplate(coupon, template);
        if (effective == null) {
            return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
        }
        return matchTemplateRule(effective, context);
    }

    /**
     * 按模板规则与结账上下文判断是否不可用；返回 null 表示规则维度通过。
     */
    public CouponCheckoutUnusableReasonEnum matchTemplateRule(CouponTemplate template, CouponCheckoutContextDTO context) {
        if (template == null || context == null) {
            return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
        }
        if (StrUtil.isNotBlank(context.getSceneCode())) {
            String expected = context.getSceneCode().trim();
            String actual = template.getSceneCode() != null ? template.getSceneCode().trim() : "";
            if (!expected.equalsIgnoreCase(actual)) {
                return CouponCheckoutUnusableReasonEnum.SCENE_MISMATCH;
            }
        }
        if (StrUtil.isNotBlank(context.getPlatform()) && StrUtil.isNotBlank(template.getUsePlatforms())) {
            List<String> platforms = parseStringList(template.getUsePlatforms());
            if (CollUtil.isNotEmpty(platforms) && platforms.stream().noneMatch(p -> p.equalsIgnoreCase(context.getPlatform().trim()))) {
                return CouponCheckoutUnusableReasonEnum.PLATFORM_LIMIT;
            }
        }
        if (StrUtil.isNotBlank(context.getMemberLevelCode()) && StrUtil.isNotBlank(template.getMemberLevels())) {
            List<String> levels = parseStringList(template.getMemberLevels());
            if (CollUtil.isNotEmpty(levels) && levels.stream().noneMatch(l -> l.equalsIgnoreCase(context.getMemberLevelCode().trim()))) {
                return CouponCheckoutUnusableReasonEnum.LEVEL_LIMIT;
            }
        }
        return matchUseScope(template, context);
    }

    private CouponCheckoutUnusableReasonEnum matchUseScope(CouponTemplate template, CouponCheckoutContextDTO context) {
        Integer useScope = template.getUseScope();
        if (useScope == null) {
            useScope = CouponUseScopeEnum.ALL.getValue();
        }
        Set<String> scopeItems = normalizeIds(context.getScopeItemIds());
        Set<String> categories = normalizeIds(context.getCategoryIds());
        Set<String> brands = normalizeIds(context.getBrandIds());
        List<String> scopeIds = parseStringList(template.getScopeIds());
        List<String> excludeIds = parseStringList(template.getExcludeScopeIds());

        if (CouponUseScopeEnum.ALL.getValue().equals(useScope)) {
            if (CollUtil.isNotEmpty(excludeIds) && CollUtil.isNotEmpty(scopeItems)) {
                Set<String> excludeSet = new HashSet<>(excludeIds);
                boolean allExcluded = scopeItems.stream().allMatch(excludeSet::contains);
                if (allExcluded) {
                    return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
                }
            }
            return null;
        }
        if (CouponUseScopeEnum.PRODUCT.getValue().equals(useScope)) {
            if (!hasIntersection(scopeIds, scopeItems)) {
                return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
            }
            return null;
        }
        if (CouponUseScopeEnum.CATEGORY.getValue().equals(useScope)) {
            if (!hasIntersection(scopeIds, categories)) {
                return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
            }
            return null;
        }
        if (CouponUseScopeEnum.BRAND.getValue().equals(useScope)) {
            if (!hasIntersection(scopeIds, brands)) {
                return CouponCheckoutUnusableReasonEnum.SCOPE_MISMATCH;
            }
            return null;
        }
        return null;
    }

    private CouponTemplate buildEffectiveTemplate(MemberCoupon coupon, CouponTemplate template) {
        boolean hasSnapshot = StrUtil.isNotBlank(coupon.getSceneCode())
                || coupon.getUseScope() != null
                || StrUtil.isNotBlank(coupon.getScopeIds())
                || StrUtil.isNotBlank(coupon.getExcludeScopeIds());
        if (template == null && !hasSnapshot) {
            return null;
        }
        CouponTemplate effective = new CouponTemplate();
        if (StrUtil.isNotBlank(coupon.getSceneCode())) {
            effective.setSceneCode(coupon.getSceneCode());
        } else if (template != null) {
            effective.setSceneCode(template.getSceneCode());
        }
        if (coupon.getUseScope() != null) {
            effective.setUseScope(coupon.getUseScope());
        } else if (template != null) {
            effective.setUseScope(template.getUseScope());
        }
        if (StrUtil.isNotBlank(coupon.getScopeIds())) {
            effective.setScopeIds(coupon.getScopeIds());
        } else if (template != null) {
            effective.setScopeIds(template.getScopeIds());
        }
        if (StrUtil.isNotBlank(coupon.getExcludeScopeIds())) {
            effective.setExcludeScopeIds(coupon.getExcludeScopeIds());
        } else if (template != null) {
            effective.setExcludeScopeIds(template.getExcludeScopeIds());
        }
        if (template != null) {
            effective.setUsePlatforms(template.getUsePlatforms());
            effective.setMemberLevels(template.getMemberLevels());
        }
        return effective;
    }

    private static boolean hasIntersection(List<String> configured, Set<String> orderIds) {
        if (CollUtil.isEmpty(configured) || CollUtil.isEmpty(orderIds)) {
            return false;
        }
        Set<String> configuredSet = new HashSet<>(configured);
        return orderIds.stream().anyMatch(configuredSet::contains);
    }

    private static Set<String> normalizeIds(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptySet();
        }
        return ids.stream()
                .filter(StrUtil::isNotBlank)
                .map(String::trim)
                .collect(Collectors.toSet());
    }

    public static List<String> parseStringList(String jsonOrCsv) {
        if (StrUtil.isBlank(jsonOrCsv)) {
            return Collections.emptyList();
        }
        String raw = jsonOrCsv.trim();
        if (raw.startsWith("[")) {
            try {
                return JSONUtil.toList(JSONUtil.parseArray(raw), String.class);
            } catch (Exception ignored) {
                return Collections.emptyList();
            }
        }
        return StrUtil.split(raw, ',').stream().map(String::trim).filter(StrUtil::isNotBlank).collect(Collectors.toList());
    }
}
