/*
 * Copyright (C) 2025 Darshan(@thisisthedarshan)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

function alignTextElements(input: string, patterns: string[]): string {
    // Split the input into lines
    const lines = input.split('\n');

    // Process each formatting pattern
    let contents = lines;
    let isEndComment = false;
    if (patterns.length == 0) {
        return input; /* Assume user doesn't want any formatting */
    }
    patterns.forEach(pattern => {
        if (pattern === "//") {
            isEndComment = true;
        }
        contents = findAndAlignConsecutivePatterns(contents, pattern, isEndComment);
    });
    // const colonFormatted = findAndAlignConsecutivePatterns(lines, ':');
    // const formattedEquals = findAndAlignConsecutivePatterns(colonFormatted, '=');
    // const formatted = findAndAlignConsecutivePatterns(formattedEquals, '//', true);

    // Join the formatted lines and return
    return contents.join('\n');
}

function findAndAlignConsecutivePatterns(lines: string[], pattern: string, isEndComment: boolean = false): string[] {
    const result = [...lines];
    let i = 0;
    let blockStart = 0;
    let inBlock = false;

    // Group lines by code blocks (determined by indentation)
    while (i < result.length) {
        // Detect blocks based on indentation or braces
        if (!inBlock && result[i].includes('{')) {
            blockStart = i;
            inBlock = true;
        }

        if (inBlock && result[i].includes('}')) {
            // Process this block separately
            processBlockForAlignment(result, blockStart, i + 1, pattern, isEndComment);
            inBlock = false;
        }

        i++;
    }

    // Process the top level (non-block) lines
    processBlockForAlignment(result, 0, result.length, pattern, isEndComment, true);

    return result;
}

function processBlockForAlignment(lines: string[], start: number, end: number, pattern: string, isEndComment: boolean, isTopLevel: boolean = false): void {
    // Find consecutive lines with the pattern at the same indent level
    let currentIndent = -1;
    let blockStartLine = start;
    let consecutiveCount = 0;

    for (let i = start; i < end; i++) {
        const line = lines[i].trim();

        // Skip empty lines or block delimiters
        if (line === '' || line === '{' || line === '}') {
            continue;
        }

        // Calculate indent level
        const indent = lines[i].length - lines[i].trimStart().length;

        // If moving to a different indent level, process the previous block
        if (currentIndent !== -1 && indent !== currentIndent) {
            if (consecutiveCount >= 2) {
                alignPatternInBlock(lines, blockStartLine, i, pattern, isEndComment);
            }
            blockStartLine = i;
            consecutiveCount = 0;
        }

        currentIndent = indent;

        // Check if line contains the pattern outside of brackets
        if (hasPatternOutsideBrackets(lines[i], pattern)) {
            consecutiveCount++;
        } else {
            // If we had consecutive lines and now found a non-matching one, process the block
            if (consecutiveCount >= 2) {
                alignPatternInBlock(lines, blockStartLine, i, pattern, isEndComment);
            }
            blockStartLine = i + 1;
            consecutiveCount = 0;
        }
    }

    // Process the last block if needed
    if (consecutiveCount >= 2) {
        alignPatternInBlock(lines, blockStartLine, end, pattern, isEndComment);
    }
}

function hasPatternOutsideBrackets(line: string, pattern: string): boolean {
    // Skip if pattern isn't in the line
    if (!line.includes(pattern)) return false;

    // Track bracket nesting and quotes
    let inSingleQuote = false;
    let inDoubleQuote = false;
    let bracketDepth = 0;  // Tracks all bracket types

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

        // Handle quotes (toggle state if not escaped)
        if (char === "'" && (i === 0 || line[i - 1] !== '\\')) {
            inSingleQuote = !inSingleQuote;
        } else if (char === '"' && (i === 0 || line[i - 1] !== '\\')) {
            inDoubleQuote = !inDoubleQuote;
        }

        // Skip processing if in quotes
        if (inSingleQuote || inDoubleQuote) continue;

        // Track bracket depth
        if (char === '(' || char === '[' || char === '{') {
            bracketDepth++;
        } else if (char === ')' || char === ']' || char === '}') {
            bracketDepth--;
        }

        // Check for pattern at current position, but only if not inside brackets
        if (bracketDepth === 0 && line.substring(i, i + pattern.length) === pattern) {
            // Verify it's a standalone pattern (not part of another token)
            const beforeChar = i > 0 ? line[i - 1] : ' ';
            const afterChar = i + pattern.length < line.length ? line[i + pattern.length] : ' ';

            if (/[\s\w]/.test(beforeChar) && /[\s\w=;,)]/.test(afterChar)) {
                return true;
            }
        }
    }

    return false;
}

function alignPatternInBlock(lines: string[], start: number, end: number, pattern: string, isEndComment: boolean): void {
    // Find the longest prefix before the pattern (ignoring patterns inside brackets)
    let maxPrefixLength = 0;
    let linesToAlign = [];

    // First, collect all valid lines and determine max prefix length
    for (let i = start; i < end; i++) {
        const line = lines[i];
        if (!hasPatternOutsideBrackets(line, pattern)) continue;

        const { prefix } = getPatternPosition(line, pattern);
        maxPrefixLength = Math.max(maxPrefixLength, prefix.length);
        linesToAlign.push(i);
    }

    // Now align each line
    for (const i of linesToAlign) {
        const line = lines[i];
        const { prefix, suffix } = getPatternPosition(line, pattern);

        const padding = ' '.repeat(maxPrefixLength - prefix.length);

        if (isEndComment) {
            // For comments, add more padding
            lines[i] = prefix + padding + ' '.repeat(4) + suffix;
        } else {
            // For other patterns, maintain a single space before and after
            lines[i] = prefix + padding + ' ' + pattern + ' ' + suffix.trimStart().substring(pattern.length).trimStart();
        }
    }
}

function getPatternPosition(line: string, pattern: string): { prefix: string, suffix: string } {
    let inSingleQuote = false;
    let inDoubleQuote = false;
    let bracketDepth = 0;

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

        // Handle quotes
        if (char === "'" && (i === 0 || line[i - 1] !== '\\')) {
            inSingleQuote = !inSingleQuote;
        } else if (char === '"' && (i === 0 || line[i - 1] !== '\\')) {
            inDoubleQuote = !inDoubleQuote;
        }

        // Skip if in quotes
        if (inSingleQuote || inDoubleQuote) continue;

        // Track bracket depth
        if (char === '(' || char === '[' || char === '{') {
            bracketDepth++;
        } else if (char === ')' || char === ']' || char === '}') {
            bracketDepth--;
        }

        // Found pattern outside brackets
        if (bracketDepth === 0 && line.substring(i, i + pattern.length) === pattern) {
            // Verify it's a standalone pattern
            const beforeChar = i > 0 ? line[i - 1] : ' ';
            const afterChar = i + pattern.length < line.length ? line[i + pattern.length] : ' ';

            if (/[\s\w]/.test(beforeChar) && /[\s\w=;,)]/.test(afterChar)) {
                const prefix = line.substring(0, i).trimEnd();
                const suffix = line.substring(i);
                return { prefix, suffix };
            }
        }
    }

    // Fallback (should not happen if hasPatternOutsideBrackets returned true)
    return { prefix: line, suffix: '' };
}

export default alignTextElements;