
package dev.sonpx.loyalty.mcp.model.valueobject;

public class NumberValue extends BaseValue<Double> {

    private static final double EPSILON = 1e-9;
    public static NumberValue ZERO = new NumberValue(0.0);

    /**
     * Constructs a new NumberValue object with the specified value.
     *
     * @param value the value to set
     */
    NumberValue(Double value) {
        super(value);
    }

    /**
     * Create a NumberValue instance from an object.
     *
     * @param value an object that could be a Number or String.
     * @return a NumberValue instance representing the specified value.
     */
    public static NumberValue of(Object value) {
        if (value instanceof Number number) {
            return new NumberValue(number.doubleValue());
        } else if (value instanceof String string) {
            return new NumberValue(Double.parseDouble(string));
        }
        return new NumberValue(null);
    }

    /**
     * Check if the current NumberValue equals another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is equal to compareValue, otherwise false.
     */
    public boolean equals(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return Math.abs(this.value - compareValue.value) < EPSILON;
    }

    /**
     * Check if the current NumberValue is not equal to another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is not equal to compareValue, otherwise false.
     */
    public boolean notEquals(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return !equals(compareValue);
    }

    /**
     * Check if the current NumberValue is less than another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is less than compareValue, otherwise false.
     */
    public boolean lessThan(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return this.value < compareValue.value;
    }

    /**
     * Check if the current NumberValue is less than or equal to another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is less than or equal to compareValue, otherwise false.
     */
    public boolean lessThanOrEqual(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return this.value < compareValue.value || equals(compareValue);
    }

    /**
     * Check if the current NumberValue is greater than another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is greater than compareValue, otherwise false.
     */
    public boolean greaterThan(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return this.value > compareValue.value;
    }

    /**
     * Check if the current NumberValue is greater than or equal to another NumberValue.
     *
     * @param compareValue the NumberValue to compare with.
     * @return true if the current NumberValue is greater than or equal to compareValue, otherwise false.
     */
    public boolean greaterThanOrEqual(NumberValue compareValue) {
        if (this.isNull()) {
            return false;
        }
        return this.value > compareValue.value || equals(compareValue);
    }

    /**
     * Check if the current NumberValue is between two other NumberValues.
     *
     * @param start the starting NumberValue (inclusive).
     * @param end the ending NumberValue (inclusive).
     * @return true if the current NumberValue is between start and end, otherwise false.
     */
    public boolean between(NumberValue start, NumberValue end) {
        if (this.isNull()) {
            return false;
        } else if (start.isNull()) {
            return lessThanOrEqual(end);
        } else if (end.isNull()) {
            return greaterThanOrEqual(start);
        }
        return this.greaterThanOrEqual(start) && this.lessThanOrEqual(end);
    }

    public Long toLong() {
        if (this.isNull()) {
            return null;
        }
        return this.value.longValue();
    }

    public NumberValue min(NumberValue with) {
        return new NumberValue(Math.min(this.value, with.value));
    }

    public NumberValue max(NumberValue with) {
        return new NumberValue(Math.max(this.value, with.value));
    }

    public NumberValue add(NumberValue with) {
        return new NumberValue(this.value + with.value);
    }

    public NumberValue subtract(NumberValue with) {
        return new NumberValue(this.value - with.value);
    }

    public NumberValue multiply(NumberValue with) {
        return new NumberValue(this.value * with.value);
    }

    public NumberValue divide(NumberValue with) {
        return new NumberValue(this.value / with.value);
    }

    public NumberValue percent(Integer value) {
        return new NumberValue((this.value * value) / 100);
    }
}
