import { XMLValidator } from 'fast-xml-parser';
import { corrigirPontuacaoECodigo, normalizarFimDeLinha, normalizarNewlinesFinais, removerBom, removerEspacosFinaisPorLinha } from './commons.js';
function tokenizeXml(src) {
    const re = /(<\?[\s\S]*?\?>|<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<!DOCTYPE[\s\S]*?>|<\/?[^>\n]+?>)/gi;
    const out = [];
    let last = 0;
    for (const metrica of src.matchAll(re)) {
        const start = metrica.index ?? -1;
        if (start < 0)
            continue;
        if (start > last)
            out.push({
                kind: 'text',
                value: src.slice(last, start)
            });
        out.push({
            kind: 'tag',
            value: metrica[0] ?? ''
        });
        last = start + (metrica[0]?.length ?? 0);
    }
    if (last < src.length)
        out.push({
            kind: 'text',
            value: src.slice(last)
        });
    return out;
}
function normalizeXmlTagToken(tag) {
    if (tag.startsWith('<!--') || tag.startsWith('<![CDATA['))
        return {
            tag,
            changed: false
        };
    if (tag.startsWith('<?'))
        return {
            tag,
            changed: false
        };
    if (tag.startsWith('<!'))
        return {
            tag,
            changed: false
        };
    let out = tag;
    const original = tag;
    out = out.replace(/^<\s+/, '<');
    out = out.replace(/^<\/\s+/, '</');
    out = out.replace(/\s+\/>$/, '/>');
    out = out.replace(/\s+>$/, '>');
    let buf = '';
    let inQuote = null;
    let prevWasSpace = false;
    for (let indice = 0; indice < out.length; indice++) {
        const ch = out[indice] ?? '';
        if (inQuote) {
            buf += ch;
            if (ch === inQuote)
                inQuote = null;
            continue;
        }
        if (ch === '"' || ch === "'") {
            inQuote = ch;
            buf += ch;
            prevWasSpace = false;
            continue;
        }
        if (/\s/.test(ch)) {
            if (!prevWasSpace) {
                buf += ' ';
                prevWasSpace = true;
            }
            continue;
        }
        prevWasSpace = false;
        buf += ch;
    }
    out = buf;
    out = out.replace(/^<\s+/, '<');
    out = out.replace(/^<\/\s+/, '</');
    out = out.replace(/\s+\/>$/, '/>');
    out = out.replace(/\s+>$/, '>');
    return {
        tag: out,
        changed: out !== original
    };
}
function hasXmlMixedContent(tokens) {
    for (const textoTitulo of tokens) {
        if (textoTitulo.kind !== 'text')
            continue;
        if (/[\S]/.test(textoTitulo.value))
            return true;
    }
    return false;
}
function prettyPrintXmlIfSafe(xml) {
    const tokens = tokenizeXml(xml);
    if (hasXmlMixedContent(tokens))
        return {
            xml,
            changed: false
        };
    let changed = false;
    let indent = 0;
    const indentStr = (node) => '  '.repeat(Math.max(0, node));
    const outLines = [];
    const pushLine = (line) => {
        if (!line)
            return;
        outLines.push(line);
    };
    for (const tok of tokens) {
        if (tok.kind === 'text') {
            continue;
        }
        const raw = tok.value.trim();
        if (!raw)
            continue;
        const isClosing = /^<\//.test(raw);
        const isSelfClosing = /\/>$/.test(raw);
        const isDecl = /^<\?xml\b/i.test(raw) || /^<\?/.test(raw);
        const isDoctype = /^<!DOCTYPE\b/i.test(raw);
        const isComment = /^<!--/.test(raw);
        const isCdata = /^<!\[CDATA\[/.test(raw);
        if (isClosing)
            indent = Math.max(0, indent - 1);
        const line = `${indentStr(indent)}${raw}`;
        pushLine(line);
        if (!isClosing && !isSelfClosing && !isDecl && !isDoctype && !isComment && !isCdata) {
            indent += 1;
        }
    }
    const out = `${outLines.join('\n').trimEnd()}\n`;
    if (out !== xml)
        changed = true;
    return {
        xml: out,
        changed
    };
}
export function formatarXmlMinimo(code) {
    const normalized = normalizarFimDeLinha(removerBom(code));
    const semEspacosFinais = removerEspacosFinaisPorLinha(normalized);
    const { text: comPontuacao, stats } = corrigirPontuacaoECodigo(semEspacosFinais);
    const tokens = tokenizeXml(comPontuacao);
    let _changedTokens = false;
    const rebuilt = tokens.map(textoTitulo => {
        if (textoTitulo.kind === 'text')
            return textoTitulo.value;
        const raiz = normalizeXmlTagToken(textoTitulo.value);
        if (raiz.changed)
            _changedTokens = true;
        return raiz.tag;
    }).join('');
    let pretty = rebuilt;
    const valid = XMLValidator.validate(pretty);
    if (valid === true) {
        const pp = prettyPrintXmlIfSafe(pretty);
        pretty = pp.xml;
    }
    else {
        pretty = `${pretty.trimEnd()}\n`;
    }
    const formatted = normalizarNewlinesFinais(pretty);
    const baseline = normalizarNewlinesFinais(normalized);
    const anyChanged = formatted !== baseline;
    return {
        ok: true,
        parser: 'xml',
        formatted,
        changed: anyChanged,
        reason: anyChanged ? `estilo-tutanus-xml(punct:${stats.pontuacaoRepetida},space:${stats.espacamentoIncorreto},ops:${stats.operadoresRelacionais})` : 'normalizacao-basica'
    };
}
//# sourceMappingURL=xml.js.map