import { BaseRuleVisitor, getTagName, getAttributes, findAttributeByName, getAttributeValue, HEADING_TAGS } from "./rule-utils.js";
class NoEmptyHeadingsVisitor extends BaseRuleVisitor {
    visitHTMLElementNode(node) {
        this.checkHeadingElement(node);
        super.visitHTMLElementNode(node);
    }
    visitHTMLSelfCloseTagNode(node) {
        this.checkSelfClosingHeading(node);
        super.visitHTMLSelfCloseTagNode(node);
    }
    checkHeadingElement(node) {
        if (!node.open_tag || node.open_tag.type !== "AST_HTML_OPEN_TAG_NODE") {
            return;
        }
        const openTag = node.open_tag;
        const tagName = getTagName(openTag);
        if (!tagName) {
            return;
        }
        const isStandardHeading = HEADING_TAGS.has(tagName);
        const isAriaHeading = this.hasHeadingRole(openTag);
        if (!isStandardHeading && !isAriaHeading) {
            return;
        }
        if (this.isEmptyHeading(node)) {
            const elementDescription = isStandardHeading
                ? `\`<${tagName}>\``
                : `\`<${tagName} role="heading">\``;
            this.addOffense(`Heading element ${elementDescription} must not be empty. Provide accessible text content for screen readers and SEO.`, node.location, "error");
        }
    }
    checkSelfClosingHeading(node) {
        const tagName = getTagName(node);
        if (!tagName) {
            return;
        }
        // Check if it's a standard heading tag (h1-h6) or has role="heading"
        const isStandardHeading = HEADING_TAGS.has(tagName);
        const isAriaHeading = this.hasHeadingRole(node);
        if (!isStandardHeading && !isAriaHeading) {
            return;
        }
        // Self-closing headings are always empty
        const elementDescription = isStandardHeading
            ? `\`<${tagName}>\``
            : `\`<${tagName} role="heading">\``;
        this.addOffense(`Heading element ${elementDescription} must not be empty. Provide accessible text content for screen readers and SEO.`, node.tag_name.location, "error");
    }
    isEmptyHeading(node) {
        if (!node.body || node.body.length === 0) {
            return true;
        }
        // Check if all content is just whitespace or inaccessible
        let hasAccessibleContent = false;
        for (const child of node.body) {
            if (child.type === "AST_LITERAL_NODE") {
                const literalNode = child;
                if (literalNode.content.trim().length > 0) {
                    hasAccessibleContent = true;
                    break;
                }
            }
            else if (child.type === "AST_HTML_TEXT_NODE") {
                const textNode = child;
                if (textNode.content.trim().length > 0) {
                    hasAccessibleContent = true;
                    break;
                }
            }
            else if (child.type === "AST_HTML_ELEMENT_NODE") {
                const elementNode = child;
                // Check if this element is accessible (not aria-hidden="true")
                if (this.isElementAccessible(elementNode)) {
                    hasAccessibleContent = true;
                    break;
                }
            }
            else {
                // If there's any non-literal/non-text/non-element content (like ERB), consider it accessible
                hasAccessibleContent = true;
                break;
            }
        }
        return !hasAccessibleContent;
    }
    hasHeadingRole(node) {
        const attributes = getAttributes(node);
        const roleAttribute = findAttributeByName(attributes, "role");
        if (!roleAttribute) {
            return false;
        }
        const roleValue = getAttributeValue(roleAttribute);
        return roleValue === "heading";
    }
    isElementAccessible(node) {
        // Check if the element has aria-hidden="true"
        if (!node.open_tag || node.open_tag.type !== "AST_HTML_OPEN_TAG_NODE") {
            return true;
        }
        const openTag = node.open_tag;
        const attributes = getAttributes(openTag);
        const ariaHiddenAttribute = findAttributeByName(attributes, "aria-hidden");
        if (ariaHiddenAttribute) {
            const ariaHiddenValue = getAttributeValue(ariaHiddenAttribute);
            if (ariaHiddenValue === "true") {
                return false;
            }
        }
        // Recursively check if the element has any accessible content
        if (!node.body || node.body.length === 0) {
            return false;
        }
        for (const child of node.body) {
            if (child.type === "AST_LITERAL_NODE") {
                const literalNode = child;
                if (literalNode.content.trim().length > 0) {
                    return true;
                }
            }
            else if (child.type === "AST_HTML_TEXT_NODE") {
                const textNode = child;
                if (textNode.content.trim().length > 0) {
                    return true;
                }
            }
            else if (child.type === "AST_HTML_ELEMENT_NODE") {
                const elementNode = child;
                if (this.isElementAccessible(elementNode)) {
                    return true;
                }
            }
            else {
                // If there's any non-literal/non-text/non-element content (like ERB), consider it accessible
                return true;
            }
        }
        return false;
    }
}
export class HTMLNoEmptyHeadingsRule {
    name = "html-no-empty-headings";
    check(node) {
        const visitor = new NoEmptyHeadingsVisitor(this.name);
        visitor.visit(node);
        return visitor.offenses;
    }
}
//# sourceMappingURL=html-no-empty-headings.js.map