export function normalizarFimDeLinha(code) {
    return code.replace(/\r\n?/g, '\n');
}
export function removerBom(code) {
    return code.length > 0 && code.charCodeAt(0) === 0xfeff ? code.slice(1) : code;
}
export function normalizarNewlinesFinais(code) {
    const normalized = normalizarFimDeLinha(code);
    return `${normalized.replace(/\n+$/g, '')}\n`;
}
export function removerEspacosFinaisPorLinha(code) {
    return removerEspacosFinaisPorLinhaComProtecao(code);
}
export function removerEspacosFinaisPorLinhaComProtecao(code, protectedLines) {
    return code.split('\n').map((linha, idx) => {
        const lineNo = idx + 1;
        if (protectedLines && protectedLines.has(lineNo))
            return linha;
        return linha.replace(/[ \t]+$/g, '');
    }).join('\n');
}
export function limitarLinhasEmBranco(code, maxConsecutivas = 2, protectedLines) {
    const lines = normalizarFimDeLinha(code).split('\n');
    const out = [];
    let consecutive = 0;
    let changed = false;
    for (let indice = 0; indice < lines.length; indice++) {
        const line = lines[indice] ?? '';
        const lineNo = indice + 1;
        if (protectedLines && protectedLines.has(lineNo)) {
            out.push(line);
            consecutive = 0;
            continue;
        }
        const isBlank = line.trim() === '';
        if (isBlank) {
            consecutive += 1;
            if (consecutive > maxConsecutivas) {
                changed = true;
                continue;
            }
        }
        else {
            consecutive = 0;
        }
        out.push(line);
    }
    return {
        code: out.join('\n'),
        changed
    };
}
const COMUM_SUBSTITUICOES = {
    '\u201c': '"',
    '\u201d': '"',
    '\u2018': "'",
    '\u2019': "'",
    '\u2013': '-',
    '\u2014': '-',
    '\u00A0': ' ',
    '\u00B4': "'"
};
export function normalizarUnicode(code) {
    let normalized = code.normalize('NFKC');
    let changed = false;
    for (const [pattern, replacement] of Object.entries(COMUM_SUBSTITUICOES)) {
        if (normalized.includes(pattern)) {
            normalized = normalized.split(pattern).join(replacement);
            changed = true;
        }
    }
    return {
        text: normalized,
        changed
    };
}
const SPACE_BEFORE_COMMA_SEMI_RE = /(\w)\s+([,;])/g;
export function corrigirEspacamentoBasicoCodigo(code) {
    let count = 0;
    const result = code.replace(SPACE_BEFORE_COMMA_SEMI_RE, (_match, p1, p2) => {
        count++;
        return `${p1}${p2}`;
    });
    return {
        text: result,
        changed: count > 0,
        count
    };
}
const REPEATABLE_TO_SINGLE = new Set([',', '.', '!', '?', ';']);
const REPEATABLE_KEEP_UP_TO_3 = new Set(['.', '!', '?']);
const MULTI_PUNCT_RE = /([,\.!?:;_\-])\1+/g;
const SPACE_BEFORE_PUNCT_RE = /\s+([,.:;!?])/g;
const NO_SPACE_AFTER_NON_DOT_PUNCT_RE = /([,:;!?])([^\s\)\]\}\[!,;:?])/g;
const SPACE_BEFORE_ELLIPSIS_RE = /([^0-9])(\.{3})/g;
const PROTECTED_PATTERNS = [/https?:\/\/[^\s"'<>]+/gi, /ftp:\/\/[^\s"'<>]+/gi, /file:\/\/[^\s"'<>]+/gi, /www\.[^\s"'<>]+/gi, /[a-zA-Z0-9](?:\.[a-zA-Z0-9-]+)+\.[a-zA-Z]{2,}/g, /(?:^|(?<=\s|["'=(\[,]))\.\//g, /(?:^|(?<=\s|["'=(\[,]))\.\.\//g, /(?<=[/\\])[\w-]+\.[\w]+/g, /\d+\.\d+(?:\.\d+)*/g, /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, /[a-f0-9]{7,}\.[a-f0-9]+/gi, /[\w-]+\.[\w-]+\.[\w]+/g,
    /(?:^|[\s"'=(\[,])([\/\\][\w-]+(?:[\/\\][\w-]+)*[\/\\]?[\w-]+\.[a-zA-Z0-9]+)/g, /(?:^|[\s"'=(\[,])([\w-]+(?:[\/\\][\w-]+)+\.[a-zA-Z0-9]+)/g,
    /(?:from\s+|require\(|import\s+)(["'])([^"']+\.[a-zA-Z0-9]+)\1/g];
function protectSequences(code) {
    const placeholders = [];
    let result = code;
    for (const pattern of PROTECTED_PATTERNS) {
        pattern.lastIndex = 0;
        result = result.replace(pattern, match => {
            const id = placeholders.length;
            placeholders.push(match);
            return `\x00PROTECTED_${id}\x00`;
        });
    }
    return {
        text: result,
        placeholders
    };
}
function restoreSequences(code, placeholders) {
    return code.replace(/\x00PROTECTED_(\d+)\x00/g, (_m, id) => {
        return placeholders[Number(id)] ?? _m;
    });
}
const NO_SPACE_AFTER_DOT_RE = /(?<![a-zA-Z0-9_.])\.(?![a-zA-Z0-9/\\.\s])/g;
function isLikelyFilePathOrExtension(line, index) {
    const before = line.slice(Math.max(0, index - 50), index);
    const after = line.slice(index + 1, Math.min(line.length, index + 10));
    if (/[a-zA-Z0-9_-]$/.test(before) && /^[a-zA-Z0-9]+$/.test(after)) {
        return true;
    }
    if (/[\/\\]$/.test(before) || /[\/\\]/.test(before.slice(-10))) {
        return true;
    }
    if (/\.[a-zA-Z0-9]+$/.test(after)) {
        return true;
    }
    if (/`[^`]*`\s*$/.test(before)) {
        return true;
    }
    if (after.trim() === '') {
        return true;
    }
    return false;
}
function isInStringOrComment(line, index) {
    const prefix = line.slice(0, index);
    const stringCount = (prefix.match(/(?<!\\)(?:\\\\)*['"`]/g) || []).length;
    if (stringCount % 2 === 1)
        return true;
    if (/\/\/[^]*$/.test(prefix))
        return true;
    const blockOpens = (prefix.match(/\/\*/g) || []).length;
    const blockCloses = (prefix.match(/\*\//g) || []).length;
    return blockOpens > blockCloses;
}
function isMarkdownCodeFence(line) {
    const trimmed = line.trim();
    return /^(`{3,}|~{3,})/.test(trimmed);
}
function isInsideMarkdownCodeBlock(lines, lineIndex) {
    let inFence = false;
    let fenceChar = null;
    let fenceLen = 0;
    for (let indice = 0; indice < lineIndex; indice++) {
        const line = lines[indice] ?? '';
        const trimmed = line.trim();
        const openMatch = trimmed.match(/^(`{3,}|~{3,})(\S*)?$/);
        if (!inFence && openMatch) {
            inFence = true;
            fenceChar = openMatch[1][0] === '~' ? '~' : '`';
            fenceLen = openMatch[1].length;
            continue;
        }
        if (inFence && fenceChar) {
            const closeMatch = trimmed.match(/^(`{3,}|~{3,})\s*$/);
            if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
                inFence = false;
                fenceChar = null;
            }
        }
    }
    return inFence;
}
function shouldSkipLineForPunctuation(line, lineIndex, lines) {
    if (isMarkdownCodeFence(line))
        return true;
    if (isInsideMarkdownCodeBlock(lines, lineIndex))
        return true;
    const trimmed = line.trim();
    if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('--'))
        return true;
    if (trimmed.startsWith('/*') || trimmed.startsWith('*/'))
        return true;
    if (/\b(?:import|export|require|from)\s+.*["'][^"']+\.[a-zA-Z0-9]+["']/.test(line))
        return true;
    if (/^(?:[\/\\]|\.\/|\.\.\/|~\/)[\w-\/\\]+\.[a-zA-Z0-9]+$/.test(trimmed))
        return true;
    if (/\*\*[^*]+\*\*/.test(line) || /(?<!\*)\*[^*\s][^*]*[^*\s]\*(?!\*)/.test(line))
        return true;
    if (/\[!\[/.test(line))
        return true;
    if (/^[\s]*\*-\s+\*\*[^*]+\*\*/.test(line))
        return true;
    return false;
}
function collapseRepeatedPunct(code) {
    const lines = code.split('\n');
    let totalCount = 0;
    let changed = false;
    const out = lines.map((line, idx) => {
        if (shouldSkipLineForPunctuation(line, idx, lines))
            return line;
        let count = 0;
        let lineChanged = false;
        let result = line;
        MULTI_PUNCT_RE.lastIndex = 0;
        let match;
        while ((match = MULTI_PUNCT_RE.exec(result)) !== null) {
            const matchIndex = match.index;
            const matchStr = match[0];
            const ch = matchStr[0];
            if (isInStringOrComment(result, matchIndex)) {
                continue;
            }
            let replacement;
            if (REPEATABLE_KEEP_UP_TO_3.has(ch)) {
                replacement = ch.repeat(Math.min(matchStr.length, 3));
            }
            else if (REPEATABLE_TO_SINGLE.has(ch)) {
                replacement = ch;
            }
            else {
                replacement = matchStr;
            }
            if (replacement !== matchStr) {
                result = result.slice(0, matchIndex) + replacement + result.slice(matchIndex + matchStr.length);
                count++;
                lineChanged = true;
                MULTI_PUNCT_RE.lastIndex = matchIndex + replacement.length;
            }
        }
        if (lineChanged)
            changed = true;
        totalCount += count;
        return result;
    });
    return {
        text: out.join('\n'),
        changed,
        count: totalCount
    };
}
function fixNonDotPunct(match, p1, _p2, offset, fullLine) {
    const charAfter = fullLine[offset + 1];
    if (charAfter && /[a-zA-Z0-9_\/\\.-]/.test(charAfter)) {
        const ctxBefore = fullLine.slice(Math.max(0, offset - 30), offset);
        const ctxAfter = fullLine.slice(offset + 1, Math.min(fullLine.length, offset + 20));
        if (/[\/\\]/.test(ctxBefore) || /[\/\\]/.test(ctxAfter) || /\w+\.\w+/.test(ctxAfter)) {
            return match;
        }
    }
    return `${p1} `;
}
function fixDotPunct(match, offset, fullLine) {
    if (isLikelyFilePathOrExtension(fullLine, offset))
        return match;
    return '. ';
}
function fixSpacingAroundPunct(code) {
    const { text: protected_code, placeholders } = protectSequences(code);
    const lines = protected_code.split('\n');
    let totalChanges = 0;
    let changed = false;
    const out = lines.map((line, idx) => {
        if (shouldSkipLineForPunctuation(line, idx, lines))
            return line;
        const t1 = line.replace(SPACE_BEFORE_PUNCT_RE, '$1');
        const t2 = t1.replace(NO_SPACE_AFTER_NON_DOT_PUNCT_RE, (m, p1, _p2, off) => fixNonDotPunct(m, p1, _p2, off, t1));
        const t3 = t2.replace(NO_SPACE_AFTER_DOT_RE, (m, off) => fixDotPunct(m, off, t2));
        const t4 = t3.replace(SPACE_BEFORE_ELLIPSIS_RE, '$1 $2');
        if (t4 !== line) {
            changed = true;
            const countBefore = (line.match(SPACE_BEFORE_PUNCT_RE) ?? []).length;
            const countNonDot = (t1.match(NO_SPACE_AFTER_NON_DOT_PUNCT_RE) ?? []).length;
            const countDot = (t2.match(NO_SPACE_AFTER_DOT_RE) ?? []).length;
            const countEllipsis = (t3.match(SPACE_BEFORE_ELLIPSIS_RE) ?? []).length;
            totalChanges += countBefore + countNonDot + countDot + countEllipsis;
        }
        return t4;
    });
    const result = restoreSequences(out.join('\n'), placeholders);
    return { text: result, changed, count: totalChanges };
}
const RELATIONAL_OPS = [{
        pattern: /<=/g,
        replacement: '≤'
    }, {
        pattern: />=/g,
        replacement: '≥'
    }, {
        pattern: /!=/g,
        replacement: '≠'
    }, {
        pattern: /=>(\s*[{])/g,
        replacement: '→$1'
    }, {
        pattern: /<-/g,
        replacement: '←'
    }, {
        pattern: /->/g,
        replacement: '→'
    }];
function normalizeRelationalOperators(code) {
    const lines = code.split('\n');
    let totalCount = 0;
    let changed = false;
    const out = lines.map((line, idx) => {
        if (shouldSkipLineForPunctuation(line, idx, lines))
            return line;
        let result = line;
        for (const { pattern, replacement } of RELATIONAL_OPS) {
            const matches = result.match(pattern);
            if (matches) {
                totalCount += matches.length;
                result = result.replace(pattern, replacement);
            }
        }
        if (result !== line)
            changed = true;
        return result;
    });
    return {
        text: out.join('\n'),
        changed,
        count: totalCount
    };
}
function isJsonOrYaml(code) {
    const trimmed = code.trim();
    const pareceJson = (trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'));
    const pareceYaml = trimmed.includes(':') && !trimmed.includes(';') && !trimmed.includes('{') && /^[a-zA-Z_]/m.test(trimmed);
    return pareceJson || pareceYaml;
}
function extractHtmlComments(code) {
    const placeholders = [];
    const text = code.replace(/<!--[\s\S]*?-->/g, match => {
        const id = placeholders.length;
        placeholders.push(match);
        return `\x00HTML_COMMENT_${id}\x00`;
    });
    return { text, placeholders };
}
function restoreHtmlComments(code, placeholders) {
    return code.replace(/\x00HTML_COMMENT_(\d+)\x00/g, (_m, id) => placeholders[Number(id)] ?? _m);
}
function skipJsonYamlResult(code) {
    return { text: code, changed: false, stats: { pontuacaoRepetida: 0, espacamentoIncorreto: 0, operadoresRelacionais: 0 } };
}
export function corrigirPontuacaoECodigo(code) {
    if (isJsonOrYaml(code))
        return skipJsonYamlResult(code);
    const { text: semHtml, placeholders } = extractHtmlComments(code);
    const { text: collapsed, count: pontuacaoRepetida } = collapseRepeatedPunct(semHtml);
    const { text: spaced, count: espacamentoIncorreto } = fixSpacingAroundPunct(collapsed);
    const { text: normalized, count: operadoresRelacionais } = normalizeRelationalOperators(spaced);
    const restored = restoreHtmlComments(normalized, placeholders);
    const changed = pontuacaoRepetida > 0 || espacamentoIncorreto > 0 || operadoresRelacionais > 0;
    return { text: restored, changed, stats: { pontuacaoRepetida, espacamentoIncorreto, operadoresRelacionais } };
}
export function corrigirPontuacaoECodigoConservador(code) {
    if (isJsonOrYaml(code))
        return skipJsonYamlResult(code);
    const { text: semHtml, placeholders } = extractHtmlComments(code);
    const { text: collapsed, count: pontuacaoRepetida } = collapseRepeatedPunct(semHtml);
    const { text: spaced, count: espacamentoIncorreto } = corrigirEspacamentoBasicoCodigo(collapsed);
    const restored = restoreHtmlComments(spaced, placeholders);
    const changed = pontuacaoRepetida > 0 || espacamentoIncorreto > 0;
    return { text: restored, changed, stats: { pontuacaoRepetida, espacamentoIncorreto, operadoresRelacionais: 0 } };
}
//# sourceMappingURL=commons.js.map