package com.usky.vpp.util;

import com.usky.common.core.exception.BusinessException;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;

/**
 * 响应量系数区间：结构化边界校验、匹配、重叠判断与展示串拼装。
 */
public final class VppResponseCoefficientHelper {

    private VppResponseCoefficientHelper() {
    }

    /**
     * 由结构化边界构建区间并校验。
     */
    public static Range of(BigDecimal lower, BigDecimal upper,
                           Boolean lowerInclusive, Boolean upperInclusive) {
        if (lower == null && upper == null) {
            throw new BusinessException("响应量系数下限与上限不能同时为空");
        }
        boolean lowerInc = lower != null && Boolean.TRUE.equals(lowerInclusive);
        boolean upperInc = upper != null && Boolean.TRUE.equals(upperInclusive);
        if (lower != null && upper != null && lower.compareTo(upper) >= 0) {
            throw new BusinessException("响应量系数区间下限必须小于上限");
        }
        return new Range(lower, upper, lowerInc, upperInc);
    }

    public static boolean matches(BigDecimal lower, BigDecimal upper,
                                  Boolean lowerInclusive, Boolean upperInclusive,
                                  BigDecimal coefficient) {
        return coefficient != null && of(lower, upper, lowerInclusive, upperInclusive).contains(coefficient);
    }

    public static boolean matches(Range range, BigDecimal coefficient) {
        return coefficient != null && range != null && range.contains(coefficient);
    }

    public static boolean overlaps(Range left, Range right) {
        return left != null && right != null && left.overlaps(right);
    }

    /** 拼装展示串，如 [0.6,0.8)、&lt;0.6、&gt;1.4。 */
    public static String format(BigDecimal lower, BigDecimal upper,
                                Boolean lowerInclusive, Boolean upperInclusive) {
        return of(lower, upper, lowerInclusive, upperInclusive).format();
    }

    public static String format(Range range) {
        return range == null ? null : range.format();
    }

    /**
     * 解析历史字符串区间，仅供数据迁移使用。
     */
    public static Range parse(String expression) {
        if (!StringUtils.hasText(expression)) {
            throw new BusinessException("响应量系数不能为空");
        }
        String value = expression.trim().replace('，', ',').replace(" ", "");
        try {
            if (value.startsWith("<=")) {
                return new Range(null, new BigDecimal(value.substring(2)), false, true);
            }
            if (value.startsWith("<")) {
                return new Range(null, new BigDecimal(value.substring(1)), false, false);
            }
            if (value.startsWith(">=")) {
                return new Range(new BigDecimal(value.substring(2)), null, true, false);
            }
            if (value.startsWith(">")) {
                return new Range(new BigDecimal(value.substring(1)), null, false, false);
            }
            if ((value.startsWith("[") || value.startsWith("("))
                    && (value.endsWith("]") || value.endsWith(")"))) {
                String[] bounds = value.substring(1, value.length() - 1).split(",", -1);
                if (bounds.length != 2) {
                    throw new IllegalArgumentException();
                }
                BigDecimal lower = new BigDecimal(bounds[0]);
                BigDecimal upper = new BigDecimal(bounds[1]);
                if (lower.compareTo(upper) >= 0) {
                    throw new BusinessException("响应量系数区间下限必须小于上限");
                }
                return new Range(lower, upper, value.startsWith("["), value.endsWith("]"));
            }
        } catch (NumberFormatException ex) {
            throw new BusinessException("响应量系数格式无效");
        }
        throw new BusinessException("响应量系数格式无效，示例：[0.6,0.8)、<0.6、>1.4");
    }

    public static final class Range {
        private final BigDecimal lower;
        private final BigDecimal upper;
        private final boolean lowerInclusive;
        private final boolean upperInclusive;

        private Range(BigDecimal lower, BigDecimal upper, boolean lowerInclusive, boolean upperInclusive) {
            this.lower = lower;
            this.upper = upper;
            this.lowerInclusive = lowerInclusive;
            this.upperInclusive = upperInclusive;
        }

        public BigDecimal getLower() {
            return lower;
        }

        public BigDecimal getUpper() {
            return upper;
        }

        public boolean isLowerInclusive() {
            return lowerInclusive;
        }

        public boolean isUpperInclusive() {
            return upperInclusive;
        }

        public boolean contains(BigDecimal value) {
            if (lower != null) {
                int comparison = value.compareTo(lower);
                if (comparison < 0 || (comparison == 0 && !lowerInclusive)) {
                    return false;
                }
            }
            if (upper != null) {
                int comparison = value.compareTo(upper);
                if (comparison > 0 || (comparison == 0 && !upperInclusive)) {
                    return false;
                }
            }
            return true;
        }

        public boolean overlaps(Range other) {
            return upperAllows(other.lower, other.lowerInclusive)
                    && other.upperAllows(lower, lowerInclusive);
        }

        public String format() {
            if (lower == null && upper != null) {
                return (upperInclusive ? "<=" : "<") + upper.stripTrailingZeros().toPlainString();
            }
            if (upper == null && lower != null) {
                return (lowerInclusive ? ">=" : ">") + lower.stripTrailingZeros().toPlainString();
            }
            String left = lowerInclusive ? "[" : "(";
            String right = upperInclusive ? "]" : ")";
            return left + lower.stripTrailingZeros().toPlainString()
                    + "," + upper.stripTrailingZeros().toPlainString() + right;
        }

        private boolean upperAllows(BigDecimal otherLower, boolean otherLowerInclusive) {
            if (upper == null || otherLower == null) {
                return true;
            }
            int comparison = upper.compareTo(otherLower);
            return comparison > 0 || (comparison == 0 && upperInclusive && otherLowerInclusive);
        }
    }
}
