import { $convertFromMarkdownString, $convertToMarkdownString, LINK, TEXT_FORMAT_TRANSFORMERS, TRANSFORMERS } from "@lexical/markdown";
import { $createTableCellNode, $createTableNode, $createTableRowNode, $isTableCellNode, $isTableNode, $isTableRowNode, TableCellHeaderStates, TableCellNode, TableNode, TableRowNode } from "@lexical/table";
import { $createParagraphNode, $isParagraphNode, $isTextNode } from "lexical";
import { $createAdmonitionNode, $isAdmonitionNode, AdmonitionNode } from "../extensions/admonition/admonition-node.js";
const TABLE_ROW_REG_EXP = /^(?:\|)(.+)(?:\|)\s?$/;
const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-+:? ?)+\|?\s?$/;
function isTableRowDivider(row) {
    return TABLE_ROW_DIVIDER_REG_EXP.test(row);
}
const TABLE = {
    dependencies: [
        TableNode,
        TableRowNode,
        TableCellNode
    ],
    export: (node)=>{
        if (!$isTableNode(node)) return null;
        const output = [];
        for (const row of node.getChildren()){
            const rowOutput = [];
            if (!$isTableRowNode(row)) continue;
            let isHeaderRow = false;
            for (const cell of row.getChildren())if ($isTableCellNode(cell)) {
                rowOutput.push($convertToMarkdownString(BYLINE_TRANSFORMERS, cell).replace(/\n/g, '\\n').trim());
                if (cell.hasHeaderState(TableCellHeaderStates.ROW)) isHeaderRow = true;
            }
            output.push(`| ${rowOutput.join(' | ')} |`);
            if (isHeaderRow) output.push(`| ${rowOutput.map(()=>'---').join(' | ')} |`);
        }
        return output.join('\n');
    },
    regExp: TABLE_ROW_REG_EXP,
    replace: (parentNode, _children, match)=>{
        if (isTableRowDivider(match[0])) {
            const table = parentNode.getPreviousSibling();
            if (!table || !$isTableNode(table)) return;
            const rows = table.getChildren();
            const lastRow = rows[rows.length - 1];
            if (!lastRow || !$isTableRowNode(lastRow)) return;
            lastRow.getChildren().forEach((cell)=>{
                if (!$isTableCellNode(cell)) return;
                cell.setHeaderStyles(TableCellHeaderStates.ROW, TableCellHeaderStates.ROW);
            });
            parentNode.remove();
            return;
        }
        const matchCells = mapToTableCells(match[0]);
        if (null == matchCells) return;
        const rows = [
            matchCells
        ];
        let sibling = parentNode.getPreviousSibling();
        let maxCells = matchCells.length;
        while(sibling){
            if (!$isParagraphNode(sibling)) break;
            if (1 !== sibling.getChildrenSize()) break;
            const firstChild = sibling.getFirstChild();
            if (!$isTextNode(firstChild)) break;
            const cells = mapToTableCells(firstChild.getTextContent());
            if (null == cells) break;
            maxCells = Math.max(maxCells, cells.length);
            rows.unshift(cells);
            const previousSibling = sibling.getPreviousSibling();
            sibling.remove();
            sibling = previousSibling;
        }
        const table = $createTableNode();
        for (const cells of rows){
            const tableRow = $createTableRowNode();
            table.append(tableRow);
            for(let i = 0; i < maxCells; i++)tableRow.append(i < cells.length ? cells[i] : $createTableCell(''));
        }
        const previousSibling = parentNode.getPreviousSibling();
        if ($isTableNode(previousSibling) && getTableColumnsSize(previousSibling) === maxCells) {
            previousSibling.append(...table.getChildren());
            parentNode.remove();
        } else parentNode.replace(table);
        table.selectEnd();
    },
    type: 'element'
};
function getTableColumnsSize(table) {
    const row = table.getFirstChild();
    return $isTableRowNode(row) ? row.getChildrenSize() : 0;
}
const $createTableCell = (textContent)=>{
    const content = textContent.replace(/\\n/g, '\n');
    const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
    $convertFromMarkdownString(content, BYLINE_TRANSFORMERS, cell);
    return cell;
};
const mapToTableCells = (textContent)=>{
    const match = textContent.match(TABLE_ROW_REG_EXP);
    if (!match?.[1]) return null;
    return match[1].split('|').map((text)=>$createTableCell(text));
};
const ADMONITION_TYPES = new Set([
    'note',
    'tip',
    'warning',
    'danger'
]);
const ADMONITION_START_REG_EXP = /^:::(note|tip|warning|danger)(?:\[([^\]]*)\])?\s*$/;
const ADMONITION_END_REG_EXP = /^:::\s*$/;
const ADMONITION_BODY_TRANSFORMERS = [
    ...TEXT_FORMAT_TRANSFORMERS,
    LINK
];
const ADMONITION = {
    dependencies: [
        AdmonitionNode
    ],
    export: (node)=>{
        if (!$isAdmonitionNode(node)) return null;
        const type = node.getAdmonitionType();
        const title = node.getTitle();
        const body = $convertToMarkdownString(ADMONITION_BODY_TRANSFORMERS, node).trim();
        const heading = title ? `:::${type}[${title}]` : `:::${type}`;
        return body ? `${heading}\n${body}\n:::` : `${heading}\n:::`;
    },
    regExpStart: ADMONITION_START_REG_EXP,
    regExpEnd: {
        optional: true,
        regExp: ADMONITION_END_REG_EXP
    },
    replace: (rootNode, children, startMatch, _endMatch, linesInBetween, isImport)=>{
        const rawType = startMatch[1];
        if (!ADMONITION_TYPES.has(rawType)) return false;
        const admonitionType = rawType;
        const title = startMatch[2] ?? '';
        const node = $createAdmonitionNode({
            admonitionType,
            title
        });
        if (null != children) node.append(...children);
        else if (null != linesInBetween) {
            const body = linesInBetween.join('\n').trim();
            if (body) $convertFromMarkdownString(body, ADMONITION_BODY_TRANSFORMERS, node);
        }
        if (0 === node.getChildrenSize()) node.append($createParagraphNode());
        rootNode.append(node);
        if (!isImport) node.selectStart();
    },
    type: 'multiline-element'
};
const BYLINE_TRANSFORMERS = [
    TABLE,
    ADMONITION,
    ...TRANSFORMERS
];
export { ADMONITION, BYLINE_TRANSFORMERS, TABLE };
