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

import com.jeesharp.business.modules.member.entity.Member;
import com.jeesharp.business.modules.member.entity.MemberLevel;
import com.jeesharp.business.modules.member.entity.MemberPointsRule;
import com.jeesharp.business.modules.member.entity.MemberPointsRuleLevel;
import com.jeesharp.business.modules.member.enums.MemberPointsCalcTypeEnum;
import com.jeesharp.business.modules.member.service.MemberLevelService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;

/**
 * 通用积分规则计算器：按 calc_type 与等级明细计算应发积分。
 *
 * @author jeesharp
 * @since 2026-06-23
 */
@Component
@AllArgsConstructor
public class MemberPointsCalculator {

    private final MemberLevelService memberLevelService;
    private final List<MemberPointsSceneHandler> sceneHandlers;

    public int calculate(Member member, MemberPointsRule rule, MemberPointsRuleLevel levelDetail,
                         Map<String, Object> context) {
        MemberPointsCalcTypeEnum calcType = MemberPointsCalcTypeEnum.fromCode(rule.getCalcType());
        if (calcType == null) {
            return 0;
        }
        if (calcType == MemberPointsCalcTypeEnum.CUSTOM) {
            return resolveHandler(rule.getSceneCode())
                    .map(h -> h.calculate(member, rule, levelDetail, context))
                    .orElse(0);
        }
        BigDecimal levelRate = shouldApplyLevelRate(rule) ? resolveLevelRate(member) : BigDecimal.ONE;
        BigDecimal extra = levelDetail != null && levelDetail.getExtraMultiplier() != null
                ? levelDetail.getExtraMultiplier() : BigDecimal.ONE;

        return switch (calcType) {
            case FIXED_BY_LEVEL -> {
                int fixed = levelDetail != null && levelDetail.getPointsValue() != null
                        ? levelDetail.getPointsValue() : 0;
                yield Math.max(0, BigDecimal.valueOf(fixed).multiply(extra)
                        .setScale(0, RoundingMode.FLOOR).intValue());
            }
            case FIXED_PLUS_LEVEL_RATE -> {
                int base = rule.getBasePoints() != null ? rule.getBasePoints() : 0;
                yield Math.max(0, BigDecimal.valueOf(base).multiply(levelRate).multiply(extra)
                        .setScale(0, RoundingMode.FLOOR).intValue());
            }
            case RATE_ON_AMOUNT -> {
                BigDecimal payAmount = extractPayAmount(context);
                BigDecimal amountRate = rule.getAmountRate() != null ? rule.getAmountRate() : BigDecimal.ZERO;
                if (payAmount == null || payAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    yield 0;
                }
                yield Math.max(0, payAmount.multiply(amountRate).multiply(levelRate).multiply(extra)
                        .setScale(0, RoundingMode.FLOOR).intValue());
            }
            default -> 0;
        };
    }

    private java.util.Optional<MemberPointsSceneHandler> resolveHandler(String sceneCode) {
        if (sceneCode == null) {
            return java.util.Optional.empty();
        }
        return sceneHandlers.stream()
                .filter(h -> sceneCode.equalsIgnoreCase(h.sceneCode()))
                .findFirst();
    }

    private boolean shouldApplyLevelRate(MemberPointsRule rule) {
        return rule.getApplyLevelRate() != null && rule.getApplyLevelRate() == 1;
    }

    private BigDecimal resolveLevelRate(Member member) {
        MemberLevel level = resolveMemberLevel(member);
        if (level != null && level.getPointsRate() != null) {
            return level.getPointsRate();
        }
        return BigDecimal.ONE;
    }

    private MemberLevel resolveMemberLevel(Member member) {
        if (member.getLevelId() != null) {
            MemberLevel byId = memberLevelService.get(member.getLevelId());
            if (byId != null) {
                return byId;
            }
        }
        if (member.getLevelCode() != null) {
            MemberLevel byCode = memberLevelService.getByLevelCode(member.getLevelCode());
            if (byCode != null) {
                return byCode;
            }
        }
        return memberLevelService.getByLevelCode("NORMAL");
    }

    private BigDecimal extractPayAmount(Map<String, Object> context) {
        if (context == null || context.get("payAmount") == null) {
            return null;
        }
        Object v = context.get("payAmount");
        if (v instanceof BigDecimal bd) {
            return bd;
        }
        if (v instanceof Number n) {
            return BigDecimal.valueOf(n.doubleValue());
        }
        if (v instanceof String s && !s.isBlank()) {
            return new BigDecimal(s.trim());
        }
        return null;
    }
}
