import { formatarJavaScriptMinimo } from './code.js';
import { corrigirPontuacaoECodigo, normalizarFimDeLinha, normalizarNewlinesFinais, removerBom } from './commons.js';
import { formatarCssMinimo } from './css.js';
const HTML_VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
const HTML_BLOCK_ELEMENTS = new Set(['html', 'head', 'body', 'title', 'meta', 'link', 'script', 'style', 'div', 'section', 'article', 'aside', 'header', 'footer', 'nav', 'main', 'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'form', 'fieldset', 'blockquote', 'pre', 'figure', 'figcaption', 'details', 'summary', 'dialog', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'dl', 'dt', 'dd', 'colgroup', 'caption', 'picture', 'video', 'audio', 'canvas', 'map', 'object', 'template', 'noscript', 'select', 'option', 'optgroup', 'textarea', 'button', 'datalist']);
const HTML_INLINE_ELEMENTS = new Set(['a', 'abbr', 'b', 'bdi', 'bdo', 'cite', 'code', 'data', 'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr', 'label']);
const _HTML_SELF_CLOSING = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
function extractTagName(tag) {
    const metrica = tag.match(/^<\/?([a-zA-Z][a-zA-Z0-9-]*)/);
    return metrica?.[1] ?? null;
}
function parseTagAttributes(tagContent) {
    const attrs = [];
    const afterTagName = tagContent.replace(/^[a-zA-Z][a-zA-Z0-9-]*/, '');
    if (!afterTagName.trim())
        return attrs;
    const regex = /\s*([a-zA-Z][a-zA-Z0-9-]*)(?:=(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
    let metrica;
    while ((metrica = regex.exec(afterTagName)) !== null) {
        const name = metrica[1];
        if (!name)
            continue;
        const value = metrica[2] ?? metrica[3] ?? metrica[4] ?? '';
        attrs.push({
            name,
            value
        });
    }
    return attrs;
}
function buildTagString(name, attrs, isClosing, isSelfClosing) {
    const parts = [];
    if (isClosing) {
        parts.push('</');
    }
    else {
        parts.push('<');
    }
    parts.push(name);
    if (!isClosing) {
        for (const attr of attrs) {
            if (attr.value) {
                parts.push(` ${attr.name}="${attr.value}"`);
            }
            else {
                parts.push(` ${attr.name}`);
            }
        }
        if (isSelfClosing) {
            parts.push('/');
        }
    }
    parts.push('>');
    return parts.join('');
}
function _formatAttributes(attrs, indent, width = 120) {
    if (attrs.length === 0)
        return [];
    const result = [];
    let currentLine = '';
    const baseIndent = '  '.repeat(indent + 1);
    for (const attr of attrs) {
        const attrStr = attr.value ? ` ${attr.name}="${attr.value}"` : ` ${attr.name}`;
        if (currentLine.length + attrStr.length > width && currentLine.length > 0) {
            result.push(currentLine);
            currentLine = baseIndent + attrStr;
        }
        else {
            currentLine += attrStr;
        }
    }
    if (currentLine) {
        result.push(currentLine);
    }
    return result;
}
function normalizeHtmlTag(tag) {
    let out = tag;
    out = out.replace(/\s*<\s*/g, '<');
    out = out.replace(/\s*>\s*/g, '>');
    out = out.replace(/\s*\/\s*>/g, '/>');
    let buf = '';
    let inQuote = null;
    let prevWasSpace = false;
    let afterOpenBracket = false;
    let justHadQuote = false;
    for (let indice = 0; indice < out.length; indice++) {
        const ch = out[indice] ?? '';
        if (ch === '<' || ch === '/') {
            afterOpenBracket = true;
            justHadQuote = false;
        }
        else if (ch === '>') {
            afterOpenBracket = false;
            justHadQuote = false;
        }
        if (inQuote) {
            buf += ch;
            if (ch === inQuote) {
                inQuote = null;
                justHadQuote = true;
            }
            continue;
        }
        if (ch === '"' || ch === "'") {
            inQuote = ch;
            buf += ch;
            prevWasSpace = false;
            afterOpenBracket = false;
            continue;
        }
        if (/\s/.test(ch)) {
            if (afterOpenBracket && !justHadQuote)
                continue;
            if (!prevWasSpace) {
                buf += ' ';
                prevWasSpace = true;
            }
            continue;
        }
        prevWasSpace = false;
        afterOpenBracket = false;
        justHadQuote = false;
        buf += ch;
    }
    return buf;
}
function parseTag(raw) {
    const isClosing = raw.startsWith('</');
    const isSelfClosing = raw.endsWith('/>') && !raw.endsWith('/>');
    const tagName = extractTagName(raw);
    const isVoid = tagName ? HTML_VOID_ELEMENTS.has(tagName.toLowerCase()) : false;
    const isBlock = tagName ? HTML_BLOCK_ELEMENTS.has(tagName.toLowerCase()) : false;
    const isInline = tagName ? HTML_INLINE_ELEMENTS.has(tagName.toLowerCase()) : false;
    const contentMatch = raw.match(/^<\/?([a-zA-Z][a-zA-Z0-9-]*)([^>]*)>?$/);
    const attrsStr = contentMatch?.[2] ?? '';
    const attrs = parseTagAttributes(attrsStr);
    return {
        raw,
        name: tagName,
        attrs,
        isClosing,
        isSelfClosing: isSelfClosing || isVoid,
        isVoid,
        isBlock,
        isInline
    };
}
function tokenizeHtml(src) {
    const tokens = [];
    const len = src.length;
    let pos = 0;
    while (pos < len) {
        if (src.startsWith('<!--', pos)) {
            const end = src.indexOf('-->', pos + 4);
            const close = end === -1 ? len : end + 3;
            tokens.push({ kind: 'comment', value: src.slice(pos, close) });
            pos = close;
            continue;
        }
        if (src.slice(pos, pos + 7).toLowerCase() === '<script') {
            const gt = src.indexOf('>', pos);
            if (gt !== -1) {
                const closeTag = src.toLowerCase().indexOf('</script>', gt + 1);
                const end = closeTag === -1 ? len : closeTag + 9;
                tokens.push({ kind: 'script', value: src.slice(pos, end) });
                pos = end;
                continue;
            }
        }
        if (src.slice(pos, pos + 6).toLowerCase() === '<style') {
            const gt = src.indexOf('>', pos);
            if (gt !== -1) {
                const closeTag = src.toLowerCase().indexOf('</style>', gt + 1);
                const end = closeTag === -1 ? len : closeTag + 8;
                tokens.push({ kind: 'style', value: src.slice(pos, end) });
                pos = end;
                continue;
            }
        }
        if (src.slice(pos, pos + 4).toLowerCase() === '<pre') {
            const gt = src.indexOf('>', pos);
            if (gt !== -1) {
                const closeTag = src.toLowerCase().indexOf('</pre>', gt + 1);
                const end = closeTag === -1 ? len : closeTag + 6;
                tokens.push({ kind: 'pre', value: src.slice(pos, end) });
                pos = end;
                continue;
            }
        }
        if (src.slice(pos, pos + 9).toLowerCase() === '<!doctype') {
            const gt = src.indexOf('>', pos);
            if (gt !== -1) {
                tokens.push({ kind: 'doctype', value: src.slice(pos, gt + 1) });
                pos = gt + 1;
                continue;
            }
        }
        const nextLt = src.indexOf('<', pos);
        if (nextLt === -1) {
            const text = src.slice(pos);
            if (text.trim()) {
                tokens.push({ kind: 'text', value: text });
            }
            pos = len;
            continue;
        }
        if (nextLt > pos) {
            const text = src.slice(pos, nextLt);
            if (text.trim()) {
                tokens.push({ kind: 'text', value: text });
            }
            pos = nextLt;
        }
        const tagEnd = src.indexOf('>', pos);
        if (tagEnd === -1) {
            const text = src.slice(pos);
            if (text.trim()) {
                tokens.push({ kind: 'text', value: text });
            }
            pos = len;
            continue;
        }
        const tagContent = src.slice(pos, tagEnd + 1);
        const tagMatch = tagContent.match(/^<\/?([a-zA-Z][a-zA-Z0-9-]*)/);
        if (tagMatch) {
            tokens.push({ kind: 'tag', value: tagContent });
            pos = tagEnd + 1;
        }
        else {
            tokens.push({ kind: 'text', value: src.slice(pos, pos + 1) });
            pos = pos + 1;
        }
    }
    return tokens;
}
const MAX_INLINE_LENGTH = 80;
const MAX_ATTRIBUTES_PER_LINE = 3;
function pushOpeningBlockTag(outLines, indent, tag) {
    const indentStr = (node) => '  '.repeat(Math.max(0, node));
    if (tag.attrs.length > 0 && tag.attrs.length <= MAX_ATTRIBUTES_PER_LINE) {
        const attrsStr = tag.attrs.map(arquivo => arquivo.value ? ` ${arquivo.name}="${arquivo.value}"` : ` ${arquivo.name}`).join('');
        outLines.push(`${indentStr(indent)}<${tag.name}${attrsStr}>`);
    }
    else if (tag.attrs.length > MAX_ATTRIBUTES_PER_LINE) {
        outLines.push(`${indentStr(indent)}<${tag.name}`);
        for (const attr of tag.attrs) {
            const attrStr = attr.value ? `${attr.name}="${attr.value}"` : `${attr.name}`;
            outLines.push(indentStr(indent + 1) + attrStr);
        }
        outLines.push(`${indentStr(indent)}>`);
    }
    else {
        outLines.push(`${indentStr(indent)}<${tag.name}>`);
    }
}
function tryFormatInlineTag(outLines, tokens, indice, indent, tag, isInline) {
    const indentStr = (node) => '  '.repeat(Math.max(0, node));
    const nextTok = tokens[indice + 1];
    const nextNextTok = tokens[indice + 2];
    if (!isInline || nextTok?.kind !== 'text' || nextNextTok?.kind !== 'tag' || !nextNextTok.value.startsWith('</'))
        return null;
    const closeTagName = extractTagName(nextNextTok.value);
    if (closeTagName?.toLowerCase() !== tag.name?.toLowerCase())
        return null;
    const content = nextTok.value.replace(/\s+/g, ' ').trim();
    const tagStr = buildTagString(tag.name ?? '', tag.attrs, false, false);
    const followingTok = tokens[indice + 3];
    if (followingTok?.kind === 'text') {
        outLines.push(`${indentStr(indent)}${tagStr}${content}</${tag.name}>${followingTok.value.replace(/\s+/g, ' ').trim()}`);
        return 4;
    }
    outLines.push(`${indentStr(indent)}${tagStr}${content}</${tag.name}>`);
    return 3;
}
function tryFormatBlockInline(outLines, tokens, indice, indent, tag) {
    const indentStr = (node) => '  '.repeat(Math.max(0, node));
    const nextTok = tokens[indice + 1];
    const nextNextTok = tokens[indice + 2];
    if (!tag.isBlock || nextTok?.kind !== 'text' || nextNextTok?.kind !== 'tag' || !nextNextTok.value.startsWith('</'))
        return null;
    const closeTagName = extractTagName(nextNextTok.value);
    if (closeTagName?.toLowerCase() !== tag.name?.toLowerCase())
        return null;
    const content = nextTok.value.replace(/\s+/g, ' ').trim();
    if (content.length <= MAX_INLINE_LENGTH && tag.attrs.length <= MAX_ATTRIBUTES_PER_LINE) {
        const tagStr = buildTagString(tag.name ?? '', tag.attrs, false, false);
        outLines.push(`${indentStr(indent)}${tagStr}${content}</${tag.name}>`);
        return 3;
    }
    if (tag.attrs.length > MAX_ATTRIBUTES_PER_LINE) {
        outLines.push(`${indentStr(indent)}<${tag.name}`);
        for (const attr of tag.attrs) {
            const attrStr = attr.value ? `${attr.name}="${attr.value}"` : `${attr.name}`;
            outLines.push(indentStr(indent + 1) + attrStr);
        }
        outLines.push(`${indentStr(indent)}>${content}</${tag.name}>`);
        return 3;
    }
    if (tag.attrs.length > 0) {
        const attrsStr = tag.attrs.map(arquivo => arquivo.value ? ` ${arquivo.name}="${arquivo.value}"` : ` ${arquivo.name}`).join('');
        outLines.push(`${indentStr(indent)}<${tag.name}${attrsStr}>`);
    }
    else {
        outLines.push(`${indentStr(indent)}<${tag.name}>`);
    }
    return null;
}
export function formatarHtmlMinimo(code) {
    const normalized = normalizarFimDeLinha(removerBom(code));
    const tokens = tokenizeHtml(normalized);
    const outLines = [];
    let indent = 0;
    const indentStr = (node) => '  '.repeat(Math.max(0, node));
    let indice = 0;
    while (indice < tokens.length) {
        const tok = tokens[indice];
        if (!tok) {
            indice++;
            continue;
        }
        if (tok.kind === 'comment') {
            const commentLines = tok.value.trim().split('\n');
            for (const cl of commentLines) {
                outLines.push(indentStr(indent) + cl.trim());
            }
            indice++;
            continue;
        }
        if (tok.kind === 'doctype') {
            outLines.push(indentStr(indent) + tok.value.toUpperCase());
            indice++;
            continue;
        }
        if (tok.kind === 'pre') {
            const openTagMatch = tok.value.match(/^(<pre\b[^>]*>)/i);
            const closeTagMatch = tok.value.match(/(<\/pre>)$/i);
            if (openTagMatch && closeTagMatch) {
                const openTag = normalizeHtmlTag(openTagMatch[1]);
                const closeTag = closeTagMatch[1];
                const innerContent = tok.value.slice(openTagMatch[1].length, -closeTagMatch[1].length);
                outLines.push(indentStr(indent) + openTag);
                const preLines = innerContent.split('\n');
                for (const pl of preLines) {
                    outLines.push(pl);
                }
                outLines.push(indentStr(indent) + closeTag);
            }
            else {
                outLines.push(indentStr(indent) + tok.value);
            }
            indice++;
            continue;
        }
        if (tok.kind === 'script' || tok.kind === 'style') {
            const isScript = tok.kind === 'script';
            const openTagMatch = tok.value.match(/^(<(?:script|style)\b[^>]*>)/i);
            const closeTagMatch = tok.value.match(/(<\/(?:script|style)>)$/i);
            if (openTagMatch && closeTagMatch) {
                const openTag = normalizeHtmlTag(openTagMatch[1]);
                const closeTag = normalizeHtmlTag(closeTagMatch[1]);
                const innerContent = tok.value.slice(openTagMatch[1].length, -closeTagMatch[1].length);
                outLines.push(indentStr(indent) + openTag);
                if (innerContent.trim()) {
                    let formattedInner = innerContent;
                    if (isScript) {
                        const res = formatarJavaScriptMinimo(innerContent, 'inline.js');
                        if (res.ok)
                            formattedInner = res.formatted;
                    }
                    else {
                        const res = formatarCssMinimo(innerContent);
                        if (res.ok)
                            formattedInner = res.formatted;
                    }
                    const contentLines = formattedInner.split('\n');
                    for (const line of contentLines) {
                        if (line.trim()) {
                            outLines.push(indentStr(indent + 1) + line.trim());
                        }
                    }
                }
                outLines.push(indentStr(indent) + closeTag);
            }
            else {
                outLines.push(indentStr(indent) + tok.value);
            }
            indice++;
            continue;
        }
        if (tok.kind === 'text') {
            const trimmed = tok.value.replace(/\s+/g, ' ').trim();
            if (trimmed) {
                outLines.push(indentStr(indent) + trimmed);
            }
            indice++;
            continue;
        }
        const tag = parseTag(normalizeHtmlTag(tok.value));
        const isInline = tag.name ? HTML_INLINE_ELEMENTS.has(tag.name.toLowerCase()) : false;
        if (tag.isVoid || tag.isSelfClosing) {
            const tagStr = buildTagString(tag.name ?? '', tag.attrs, false, tag.isVoid || tag.isSelfClosing);
            outLines.push(indentStr(indent) + tagStr);
            indice++;
            continue;
        }
        if (tag.isClosing) {
            if (indent > 0)
                indent--;
            outLines.push(indentStr(indent) + tag.raw);
            indice++;
            continue;
        }
        const inlineAdvance = tryFormatInlineTag(outLines, tokens, indice, indent, tag, isInline);
        if (inlineAdvance !== null) {
            indice += inlineAdvance;
            continue;
        }
        const blockAdvance = tryFormatBlockInline(outLines, tokens, indice, indent, tag);
        if (blockAdvance !== null) {
            indice += blockAdvance;
            continue;
        }
        if (tag.isBlock) {
            pushOpeningBlockTag(outLines, indent, tag);
            indent++;
            indice++;
            continue;
        }
        const tagStr = buildTagString(tag.name ?? '', tag.attrs, tag.isClosing, tag.isSelfClosing);
        outLines.push(indentStr(indent) + tagStr);
        indice++;
    }
    const { text: comPontuacao, stats } = corrigirPontuacaoECodigo(outLines.join('\n'));
    const formatted = normalizarNewlinesFinais(comPontuacao);
    const baseline = normalizarNewlinesFinais(normalized);
    const anyChanged = formatted !== baseline;
    return {
        ok: true,
        parser: 'html',
        formatted,
        changed: anyChanged,
        reason: anyChanged ? `estilo-tutanus-html(punct:${stats.pontuacaoRepetida},space:${stats.espacamentoIncorreto},ops:${stats.operadoresRelacionais})` : 'normalizacao-basica'
    };
}
//# sourceMappingURL=html.js.map