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

import java.util.Arrays;
import java.util.regex.PatternSyntaxException;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;

@Slf4j
@ToString
public class StringValue extends BaseValue<String> {

    public static final StringValue EMPTY = new StringValue("");
    public static final String PIPE_DELIMITED = "|";

    private StringValue(String value) {
        super(value);
    }

    /**
     * Create a StringValue instance from an object.
     *
     * @param value the value to convert (String, EnumBase, or other objects).
     * @return a StringValue instance or EMPTY if the value is null.
     */
    public static StringValue of(Object value) {
        if (value == null) {
            return EMPTY;
        } else if (value instanceof String string) {
            return new StringValue(string.toLowerCase().trim());
        }
        return EMPTY;
    }

    /**
     * Convert a glob pattern to a regex pattern.
     *
     * @param glob the glob pattern to convert.
     * @return a regex pattern equivalent to the glob.
     */
    public static String createRegexFromGlob(String glob) {
        StringBuilder out = new StringBuilder();
        for (int i = 0; i < glob.length(); ++i) {
            final char c = glob.charAt(i);
            switch (c) {
                case '*':
                    out.append(".*");
                    break;
                case '?':
                    out.append('.');
                    break;
                case '.':
                    out.append("\\.");
                    break;
                case '\\':
                    out.append("\\\\");
                    break;
                case '|':
                    out.append("\\|");
                    break;
                default:
                    out.append(c);
            }
        }
        return "^" + out + "$";
    }

    /**
     * Check if the current StringValue equals another StringValue.
     *
     * @param valueCompare the StringValue to compare with.
     * @return true if both values are equal, otherwise false.
     */
    public boolean equals(StringValue valueCompare) {
        return Strings.CI.equals(this.value, valueCompare.value);
    }

    /**
     * Check if the current StringValue is not equal to another StringValue.
     *
     * @param valueCompare the StringValue to compare with.
     * @return true if both values are not equal, otherwise false.
     */
    public boolean notEquals(StringValue valueCompare) {
        return !equals(valueCompare);
    }

    /**
     * Check if the current StringValue is empty or null.
     *
     * @return true if the current StringValue is empty or null, otherwise false.
     */
    public boolean isEmpty() {
        return StringUtils.isBlank(value);
    }

    /**
     * Check if the current StringValue is not empty.
     *
     * @return true if the current StringValue is not empty, otherwise false.
     */
    public boolean isNotEmpty() {
        return StringUtils.isNotEmpty(value);
    }

    /**
     * Check if the current StringValue contains another StringValue.
     *
     * @param valueCompares the StringValue to check.
     * @return true if the current StringValue contains valueCompare, otherwise false.
     */
    public boolean contains(StringValue... valueCompares) {
        for (StringValue valueCompare : valueCompares) {
            if (regularExpression(valueCompare)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the current StringValue does not contain another StringValue.
     *
     * @param valueCompares the StringValue to check.
     * @return true if the current StringValue does not contain valueCompare, otherwise false.
     */
    public boolean doesNotContains(StringValue... valueCompares) {
        return !contains(valueCompares);
    }

    /**
     * Check if the current StringValue begins with another StringValue.
     *
     * @param valueCompares the StringValue to check.
     * @return true if the current StringValue begins with valueCompare, otherwise false.
     */
    public boolean beginsWith(StringValue... valueCompares) {
        for (StringValue valueCompare : valueCompares) {
            if (Strings.CI.startsWith(this.value, valueCompare.getValue())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the current StringValue ends with another StringValue.
     *
     * @param filterValues the StringValue to check.
     * @return true if the current StringValue ends with valueCompare, otherwise false.
     */
    public boolean endsWith(StringValue... filterValues) {
        for (StringValue valueCompare : filterValues) {
            boolean isEndWith = Strings.CI.endsWith(this.value, valueCompare.getValue());
            if (isEndWith) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the current StringValue does not begin with another StringValue.
     *
     * @param valueCompares the StringValue to check.
     * @return true if the current StringValue does not begin with valueCompare, otherwise false.
     */
    public boolean doesNotBeginWith(StringValue... valueCompares) {
        for (StringValue valueCompare : valueCompares) {
            boolean hasStartsWith = Strings.CI.startsWith(this.value, valueCompare.getValue());
            if (hasStartsWith) {
                return false;
            }
        }
        return true;
    }

    /**
     * Check if the current StringValue does not end with another StringValue.
     *
     * @param filterValues the StringValue to check.
     * @return true if the current StringValue does not end with valueCompare, otherwise false.
     */
    public boolean doesNotEndsWith(StringValue... filterValues) {
        for (StringValue valueCompare : filterValues) {
            if (Strings.CI.endsWith(this.value, valueCompare.getValue())) {
                return false;
            }
        }
        return true;
    }

    /**
     * Check if the current StringValue matches a regular expression pattern.
     *
     * @param valueCompare the StringValue pattern to match.
     * @return true if the current StringValue matches valueCompare, otherwise false.
     */
    public boolean regularExpression(StringValue valueCompare) {
        String regex = createRegexFromGlob(valueCompare.value);
        try {
            // Java Escape Characters: Replace newline and tab characters with spaces
            value = value.replaceAll("[\\n\\t\\r]", " ");
            return value.matches(regex);
        } catch (PatternSyntaxException patternSyntaxException) {
            log.warn("Invalid regular expression: {}", regex);
            return false;
        }
    }

    /**
     * Check if the current StringValue is in a list of StringValues.
     *
     * @param filterValues the list of StringValues to check.
     * @return true if the current StringValue is in valueCompare, otherwise false.
     */
    public boolean in(StringValue... filterValues) {
        if (filterValues == null) {
            return false;
        }
        boolean isMatched = false;

        for (StringValue filterValue : filterValues) {
            if (equals(filterValue)) {
                isMatched = true;
                break;
            }
        }
        return isMatched;
    }

    /**
     * Check if the current StringValue is not in a list of StringValues.
     *
     * @param valueCompare the list of StringValues to check.
     * @return true if the current StringValue is not in valueCompare, otherwise false.
     */
    public boolean notIn(StringValue... valueCompare) {
        return !in(valueCompare);
    }

    public static StringValue[] split(Object object) {
        if (object instanceof String string) {
            return Arrays.stream(string.split("\\|")).map(StringValue::of).toArray(StringValue[]::new);
        }
        return null;
    }
}
