"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkdownConverter = void 0;
const handlers_1 = require("./handlers.js");
const helpers_1 = require("./helpers.js");
/**
 * Main converter class that implements the visitor interface to convert HTML to Markdown.
 * Uses the Chain of Responsibility pattern for handling different node types.
 */
class MarkdownConverter {
    handler;
    constructor() {
        // Set up the chain of responsibility for handling different node types
        this.handler = this.setupHandlerChain();
    }
    /**
     * Converts a text node to Markdown, escaping special characters.
     */
    visitText(node) {
        return (node.textContent || '').replace(/\n+/g, '').replace(/([<>])/g, '\\$1');
    }
    /**
     * Converts an HTML anchor element to Markdown link syntax.
     */
    visitLink(node) {
        const linkText = this.collectTextContent(node);
        const url = node.href || '';
        // Handle links with formatted content vs plain text differently
        const formattedText = node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE
            ? (0, helpers_1.applyFormatting)(linkText, node) // Plain text link
            : Array.from(node.childNodes)
                .map((child) => {
                if (child.nodeType === Node.ELEMENT_NODE) {
                    return (0, helpers_1.applyFormatting)(child.textContent || '', child);
                }
                return child.textContent || '';
            })
                .join(''); // Apply formatting for each formatted child node
        return `[${formattedText}](${url} "${linkText.replace(/"/g, '\\"')}")`;
    }
    /**
     * Converts an HTML heading element to Markdown heading syntax.
     */
    visitHeader(node, level) {
        const headerContent = this.collectTextContent(node);
        return '#'.repeat(level) + ' ' + headerContent + '\n';
    }
    /**
     * Converts an HTML paragraph to Markdown format.
     */
    visitParagraph(node) {
        const content = this.processChildren(node);
        return content.trim() + '\n\n';
    }
    /**
     * Applies Markdown formatting (bold, italic, etc.) to text content.
     */
    visitFormatting(node) {
        if (node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE) {
            const text = this.collectTextContent(node);
            return (0, helpers_1.applyFormatting)(text, node);
        }
        return (0, helpers_1.applyFormatting)(this.visitGeneric(node), node);
    }
    /**
     * Converts HTML code elements to Markdown inline code syntax.
     */
    visitCode(node) {
        const codeContent = this.collectCodeContent(node);
        if (codeContent.includes('\n')) {
            return '```\n' + codeContent + '\n```\n';
        }
        else if (codeContent.includes('`')) {
            return '`` ' + codeContent + ' ``';
        }
        else {
            return `\`${codeContent}\``;
        }
    }
    /**
     * Handles generic HTML elements by processing their children.
     */
    visitGeneric(node) {
        return this.processChildren(node);
    }
    /**
     * Converts an HTML div element to Markdown format, adding a single newline.
     */
    visitDiv(node) {
        const content = this.processChildren(node);
        return content + '\n'; // Add a single newline for <div>
    }
    /**
     * Converts an HTML br element to a newline in Markdown.
     */
    visitBr() {
        return '\n'; // Single newline for <br>
    }
    /**
     * Converts an HTML table row element to Markdown table row format.
     */
    visitTableRow(node) {
        const cells = Array.from(node.children).map((cell) => {
            return this.visitGeneric(cell).trim() || '';
        });
        return '||\n' + cells.join('\n|\n') + '\n||';
    }
    /**
     * Converts an HTML table element to Markdown table format.
     */
    visitTable(node) {
        const rows = [];
        const tableRows = Array.from(node.querySelectorAll('tr'));
        tableRows.forEach((row) => {
            rows.push(this.visitTableRow(row));
        });
        return '\n\n#|\n' + rows.join('\n') + '\n|#\n\n';
    }
    /**
     * Converts img tag to Markdown image format
     */
    visitImage(node) {
        const imgElement = node;
        const altText = imgElement.alt || '';
        const src = imgElement.src || '';
        return `![${altText}](${src})`;
    }
    /**
     * Processes a single node using the handler chain.
     */
    processNode(node) {
        const result = this.getHandler().handle(node, this);
        return result;
    }
    /**
     * Creates and links together handlers in a specific order implementing the Chain of Responsibility pattern.
     * @returns The first handler in the chain
     */
    setupHandlerChain() {
        // Create handlers for each type of node
        const textHandler = new handlers_1.TextNodeHandler();
        const linkHandler = new handlers_1.LinkHandler();
        const headerHandler = new handlers_1.HeaderHandler();
        const paragraphHandler = new handlers_1.ParagraphHandler();
        const formattingHandler = new handlers_1.FormattingHandler();
        const codeHandler = new handlers_1.CodeHandler();
        const genericHandler = new handlers_1.GenericHandler();
        const orderedListHandler = new handlers_1.OrderedListHandler();
        const unorderedListHandler = new handlers_1.UnorderedListHandler();
        const divHandler = new handlers_1.DivHandler();
        const brHandler = new handlers_1.BrHandler();
        const tableRowHandler = new handlers_1.TableRowHandler();
        const tableHandler = new handlers_1.TableHandler();
        const imageHandler = new handlers_1.ImageHandler(); // New handler for <img>
        // Chain handlers together in priority order
        textHandler
            .setNext(linkHandler)
            .setNext(headerHandler)
            .setNext(paragraphHandler)
            .setNext(divHandler)
            .setNext(brHandler)
            .setNext(orderedListHandler)
            .setNext(unorderedListHandler)
            .setNext(formattingHandler)
            .setNext(codeHandler)
            .setNext(imageHandler) // Add image handler
            .setNext(tableHandler)
            .setNext(tableRowHandler)
            .setNext(genericHandler);
        return textHandler;
    }
    /**
     * Recursively collects and processes text content from a node and its children.
     */
    collectTextContent(node) {
        // handle seo elements (hide it's content)
        if (node.className === 'visually-hidden') {
            return '';
        }
        if (node.nodeType === Node.TEXT_NODE) {
            return this.visitText(node);
        }
        return Array.from(node.childNodes)
            .map((child) => this.collectTextContent(child))
            .join('');
    }
    /**
     * Collects raw text content from code elements.
     */
    collectCodeContent(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            return node.textContent || '';
        }
        return Array.from(node.childNodes)
            .map((child) => this.collectCodeContent(child))
            .join('');
    }
    /**
     * Processes all child nodes of a given node.
     */
    processChildren(node) {
        return Array.from(node.childNodes)
            .map((child) => this.processNode(child))
            .join('');
    }
    /**
     * Gets the first handler in the chain.
     */
    getHandler() {
        return this.handler;
    }
}
exports.MarkdownConverter = MarkdownConverter;
//# sourceMappingURL=converters.js.map
