export class StringUtil {
    //==================================================================================
    /** PascalCase / camelCase / UPPER_SNAKE / lower_snake / kebab-case -> lower_snake
    *///================================================================================
    // ! NOTE: 벤치마크 결과 정규식 사용 방식에 비해 30% ~ 200% 성능 향상
    public static ToLowerSnake(word: string) {
        if(StringUtil.IsNullOrWhiteSpace(word)) return word;

        let result = '';
        let prevLowerOrNum = false;

        for (let i = 0; i < word.length; i++) {
            const char = word[i];

            if (char === '-' || char === ' ' || char === '_') {
                if (result[result.length - 1] !== '_') result += '_';
                prevLowerOrNum = false;
                continue;
            }

            if (char >= 'A' && char <= 'Z') {
                if (prevLowerOrNum && result[result.length - 1] !== '_') result += '_';
                result += char.toLowerCase();
                prevLowerOrNum = false;
            } else {
                result += char;
                prevLowerOrNum = (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9');
            }
        }

        return result;
    }

    //==================================================================================
    /** PascalCase / camelCase / UPPER_SNAKE / lower_snake / kebab-case -> UPPSER_SNAKE
    *///================================================================================
    public static ToUppserSnake(word: string) {
        return StringUtil.ToLowerSnake(word).toUpperCase();
    }
    
    //==================================================================================
    /** PascalCase / camelCase / UPPER_SNAKE / lower_snake / kebab-case -> kebab-case
    *///================================================================================
    public static ToKebabCase(word: string) {
        if(StringUtil.IsNullOrWhiteSpace(word)) return word;

        let result = '';
        let prevLowerOrNum = false;

        for (let i = 0; i < word.length; i++) {
            const char = word[i];

            if (char === '-' || char === ' ' || char === '_') {
                if (result[result.length - 1] !== '-') result += '-';
                prevLowerOrNum = false;
                continue;
            }

            if (char >= 'A' && char <= 'Z') {
                if (prevLowerOrNum && result[result.length - 1] !== '-') result += '-';
                result += char.toLowerCase();
                prevLowerOrNum = false;
            } else {
                result += char;
                prevLowerOrNum = (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9');
            }
        }

        return result;
    }
    
    //==================================================================================
    /** PascalCase / camelCase / UPPER_SKANE / lower_snake / kebab-case -> camelCase
    *///================================================================================
    /** 모든 형태의 문자열을 camelCase로 변환 */
    public static ToCamelCase(word: string): string {
        if (StringUtil.IsNullOrWhiteSpace(word)) return word;
        const words = StringUtil.ToWords(word);
        return words[0] + words.slice(1).map(w => w[0].toUpperCase() + w.slice(1)).join('');
    }

    //==================================================================================
    /** PascalCase / camelCase / UPPER_SKANE / lower_snake / kebab-case -> PascalCase
    *///================================================================================
    public static ToPascalCase(word: string): string {
        if (StringUtil.IsNullOrWhiteSpace(word)) return word;
        return StringUtil.ToWords(word).map(w => w[0].toUpperCase() + w.slice(1)).join('');
    }

    //==================================================================================
    /** snake_case / SNAKE_CASE -> camelCase
    *///================================================================================
    public static SnakeToCamel(snakeCase: string) {
        return snakeCase.toLowerCase()
            .replace(/_+([a-z])/g, (m, $1) => $1.toUpperCase());
    };

    //==================================================================================
    /** snake_case / SNAKE_CASE -> PascalCase
    *///================================================================================
    public static SnakeToPascal(snakeCase: string) {
        const camel = snakeCase.toLowerCase()
            .replace(/_+([a-z])/g, (m, $1) => $1.toUpperCase());
        return camel[0].toUpperCase() + camel.substring(1);
    };

    //==================================================================================
    /** 대상이 null이거나 빈 문자열인지 검사
    *///================================================================================
    public static IsNullOrEmpty(str: string): boolean {
        return (str == null) || str.length === 0;
    }

    //==================================================================================
    /** 대상이 null이거나 빈 문자열이거나 공백으로만 이루어진 문자열인지 검사
    *///================================================================================
    public static IsNullOrWhiteSpace(str: string): boolean {
        return !str || !str.trim();
    }

    //==================================================================================
    /** str이 words의 문자열 중 하나라도 포함하는지 검사
    *///================================================================================
    public static IncludeAny(str: string, words: string[]): boolean {
        return words.some(word => str.includes(word));
    }
    
    /** 문자열을 단어 배열로 분리 (모든 케이스 대응) */
    public static ToWords(word: string): string[] {
        const words: string[] = [];
        let currentWord = '';

        for (let i = 0; i < word.length; i++) {
            const char = word[i];
            const prevChar = i > 0 ? word[i - 1] : '';

            if (['_', '-', ' '].includes(char)) {
                if (currentWord) {
                    words.push(currentWord);
                    currentWord = '';
                }
                continue;
            }

            const isUpper = char >= 'A' && char <= 'Z';
            const isPrevLowerOrDigit = prevChar >= 'a' && prevChar <= 'z' || prevChar >= '0' && prevChar <= '9';

            if (isUpper && currentWord && isPrevLowerOrDigit) {
                words.push(currentWord);
                currentWord = char;
            } else {
                currentWord += char;
            }
        }

        if (currentWord) {
            words.push(currentWord);
        }

        return words.map(w => w.toLowerCase());
    }
}