package com.qmth.boot.core.rateLimit.entity;

import com.qmth.boot.core.rateLimit.annotation.RateLimit;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.convert.DurationStyle;

import java.time.Duration;

/**
 * 限流规则对象，可以基于标准表达式构建
 * 表达式格式为：100/2s，斜杠左边为数量，右边为时间间隔
 * 可选参数格式：100/id/2s，i表示单实例，d表示单设备
 * 可选参数格式：100/u/0s，u表示单用户，0s表示全局并发控制
 * 未配置情况下，默认参数为集群环境下的所有设备
 */
public class RateLimitRule {

    private static final String EXPRESSION_SPLIT = "/";

    private int count;

    private RateLimitScope scope;

    private RateLimitTarget target;

    private Duration period;

    public static RateLimitRule parse(RateLimit annotation) {
        return new RateLimitRule(annotation.count(), annotation.scope(), annotation.target(),
                Duration.ofMillis(annotation.period()));
    }

    public static RateLimitRule parse(String expression) {
        String[] values = StringUtils.split(StringUtils.trimToNull(expression), EXPRESSION_SPLIT);
        if (values != null) {
            String number = null;
            String duration = null;
            RateLimitScope scope = RateLimitScope.CLUSTER;
            RateLimitTarget target = RateLimitTarget.ALL;
            if (values.length == 2) {
                number = values[0];
                duration = values[1];
            } else if (values.length == 3) {
                number = values[0];
                duration = values[2];
                char[] custom = values[1].toCharArray();
                for (char c : custom) {
                    RateLimitScope s = RateLimitScope.findByCode(c);
                    if (s != null) {
                        scope = s;
                    }
                    RateLimitTarget t = RateLimitTarget.findByCode(c);
                    if (t != null) {
                        target = t;
                    }
                }
            }
            try {
                int count = Integer.parseInt(number);
                Duration period = DurationStyle.detectAndParse(duration);
                if (count > 0 && period != null) {
                    return new RateLimitRule(count, scope, target, period);
                }
            } catch (Exception e) {
            }
        }
        return null;
    }

    public RateLimitRule(int count, RateLimitScope scope, RateLimitTarget target, Duration period) {
        this.count = count;
        this.scope = scope;
        this.target = target;
        this.period = period;
    }

    public int getCount() {
        return count;
    }

    public Duration getPeriod() {
        return period;
    }

    public RateLimitScope getScope() {
        return scope;
    }

    public RateLimitTarget getTarget() {
        return target;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(count).append(EXPRESSION_SPLIT);
        if (scope != RateLimitScope.CLUSTER && target != RateLimitTarget.ALL) {
            sb.append(scope.getCode()).append(target.getCode()).append(EXPRESSION_SPLIT);
        }
        sb.append(DurationStyle.SIMPLE.print(period));
        return sb.toString();
    }
}
