import { unified } from 'unified'
import parse from 'remark-parse'
import { visit } from 'unist-util-visit'
import remarkgfm from 'remark-gfm' //是markdown支持表格、选择框、连接等相关

const compile = (text: any) => {
    let content = text || ''
    const re = /`\{\{(.*?)\}\}/
    let exec: any;
    while ((exec = re.exec(content))) {
        content = content.replace(re, '`\n' + `#${exec[0].substring(1)}\n`);
    }
    exec = null
    const reg = /#[desc]\s+/
    while ((exec = reg.exec(content))) {
        const s = exec[0]
        content = content.replace(reg, `# -${s.substring(1)}`)
    }
    // content = content.replace(/\n/g, '<br/>')
    return content;
}

const code = (): any => {
    return (tree: any) => {
        visit(tree, 'paragraph', (node: any, index: any, parent: any) => {
            const str = node?.children[0]?.value;
            if (/#\{\{(.*?)\}\}/.test(str)) {
                const code = parent?.children[index - 1];
                if (code) {
                    code.properties = properties(str);
                    parent.children.splice(index, 1);
                    return;
                }
            }
        });
        return tree;
    };
}

const inlineCode = (): any => {
    return (tree: any) => {
        visit(tree, 'inlineCode', (node: any, index: any, parent: any) => {
            const next = index < parent?.children?.length - 1 ? parent?.children[index + 1] : null;
            if (next) {
                const str = next.value;
                if (/#\{\{(.*?)\}\}/.test(str)) {
                    node.properties = properties(str);
                    parent.children.splice(index + 1, 1);
                }
            }
        });
        return tree;
    };
}

const captions = (): any => {
    return (tree: any) => {
        visit(tree, 'text', (_node: any, _index: any, _parent: any) => {
            if ('paragraph' === _parent?.type) {
                const value = _node?.value
                const re = /(\[.*\].*)/g
                const exsit = re.test(value)
                if (!exsit) {
                    return
                }
                const children: any[] = []

                const regex = /(\[.*\].*)/g
                const list = value?.split(regex)

                for (let s of list) {
                    if (!regex.test(s)) {
                        const n = {
                            type: 'text',
                            value: s
                        }
                        children.push(n)
                        continue
                    }
                    const reg = /(\[.*\])(.*)/g
                    const ts = (reg?.exec(s) ?? ['', '[]'])[1]?.replace(/[\[\]]/g, '')?.split('-')
                    const n = {
                        type: 'captions',
                        properties: {
                            st: ts[0] ?? '',
                            et: ts[1] ?? ''
                        },
                        value: s?.replace(/\[.*\]/g, '')
                    }
                    children.push(n)
                }
                _parent.children.splice(_index, 1, ...children)
            }
        });
        return tree;
    };
}

const heading = (): any => {
    return (tree: any) => {
        visit(tree, 'text', (_node: any, _index: any, _parent: any) => {
            if ('heading' === _parent?.type) {
                const value = _node?.value
                const re = /^(-[desc])/g
                if (!re.test(value)) {
                    return
                }
                const args = value?.split(re).filter((it: any) => it)
                if (!args || args?.length == 0) {
                    return
                }
                const tag = args[0]?.substring(1)?.trim() ?? ''
                const text = args[1]?.trim() ?? ''
                _node.value = text
                _parent.properties = {
                    tag: tag
                }
            }
        })
        return tree
    }
}

const traverse = (children: any, data: any) => {
    for (let i = 0; i < children?.length; i++) {
        if ('paragraph' === children[i]?.type) {
            traverse(children[i]?.children, data);
        } else {
            data.push(children[i])
        }
    }
}

const build = (target: any, root: any) => {
    if (!root?.next) {
        root.next = []
        root.next.push(target)
        return
    }
    const last = root?.next[root?.next?.length - 1]
    if (last?.depth < target?.depth) {
        build(target, last)
    } else {
        root?.next?.push(target)
    }
}

const deepClone = (obj: any) => {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    let newObj: any = Array.isArray(obj) ? [] : {};
    for (let key in obj) {
        newObj[key] = deepClone(obj[key]);
    }
    return newObj;
}

const timer = (str: string) => {
    try {
        if (!str) {
            return undefined
        }
        if (str.endsWith('.')) {
            str = str.substring(0, str.length - 1)
        }
        let ms: number = 0
        let s: string = str
        if (str.includes('.')) {
            ms = Number(str.substring(str.lastIndexOf('.') + 1))
            s = str.substring(0, str.lastIndexOf('.'))
        }
        const t = s.split(':')
        let ts: number = 0
        for (let i = 0; i < t.length; i++) {
            ts += Number(t[t.length - 1 - i]) * Math.pow(60, i)
        }
        return ts + ms / 1000
    } catch (error) {
        console.error(error)
        return undefined
    }
}

const properties = (str: any) => {
    const match = str?.match(/#\{\{(.*?)\}\}/);
    return {
        ext: match[1].trim(),
    };
}

const Mdast = {

    parse: (text?: string) => {
        const content = compile(text || '')


        const processer = unified()
            .use(parse)
            .use(remarkgfm)
            .use(code)
            .use(inlineCode)
            .use(heading)
            .use(captions)

        const tree = processer.parse(content)
        const ast = processer.runSync(tree)
        processer.freeze()
        return ast
    },

    captions: (mdast: any) => {
        const _mdast = deepClone(mdast)
        const data: any = [];
        const processer = unified().use(() => (tree: any) => {
            visit(tree, 'captions', (node: any) => {
                const properties = node?.properties;
                const item = {
                    st: timer(properties?.st),
                    et: timer(properties?.et),
                    value: node?.value,
                };
                data.push(item);
            });
        });
        processer.runSync(_mdast);
        processer.freeze();
        return data;
    },

    actives: (mdast: any) => {
        const _mdast = deepClone(mdast)
        const data: any = []
        const processer = unified()
            .use(() => (tree: any) => {
                visit(tree, 'code', (node: any) => {
                    const ext: string = node.properties?.ext
                    if (ext?.startsWith('active')) {
                        const item = {
                            lang: node?.lang,
                            st: ext?.startsWith('active ') ? timer(ext?.substring(7).trim()) : undefined,
                            et: undefined,
                            value: node?.value?.trim(),
                            position: node?.position
                        }
                        data.push(item)
                    }
                })
            })
        processer.runSync(_mdast)
        processer.freeze()
        return data
    },

    heading: {
        list: (mdast: any) => {
            const _mdast = deepClone(mdast)
            let curr: any = null
            const children: any = []
            traverse(_mdast.children, children)
            children?.forEach((node: any) => {
                if ('heading' === node?.type) {
                    node.properties = node?.properties || {}
                    node.properties.stlist = []
                    node.properties.etlist = []
                    curr = null
                    curr = node
                }
                if ('captions' === node?.type && curr && curr?.properties) {
                    curr?.properties?.stlist.push(node?.properties?.st)
                    curr?.properties?.etlist.push(node?.properties?.et)
                }
            });

            children.forEach((node: any, _i: number) => {
                if ('heading' === node?.type && node?.properties?.stlist?.length === 0) {
                    let j = _i + 1;
                    while (j < _mdast?.children?.length) {
                        const child = _mdast?.children[j]
                        if ('heading' === child?.type && child?.properties?.stlist?.length > 0) {
                            node?.properties?.stlist.push(...(child?.properties?.stlist ?? []))
                            node?.properties?.etlist.push(...(child?.properties?.etlist ?? []))
                        }
                        j++;
                    }
                }
            })

            const arry: any = []

            children.forEach((node: any) => {
                if ('heading' === node?.type) {
                    const st = node?.properties?.stlist.map((s: any) => timer(s)).sort((a: any, b: any) => a - b)[0]
                    const et = node?.properties?.etlist.map((s: any) => timer(s)).sort((a: any, b: any) => b - a)[0]
                    node.properties.st = st
                    node.properties.et = et
                    delete node?.properties?.stlist
                    delete node?.properties?.etlist
                    arry.push(node)
                }
            })
            return arry;
        },

        tree: (mdast: any) => {
            const heading: Array<any> = Mdast.heading.list(mdast)
            let c: any = null
            let children: any = []
            heading.forEach((h: any, _i: number) => {
                if (c === null || h?.depth <= c?.depth) {
                    c = deepClone(h)
                    c.next = []
                    children?.push(c)
                } else {
                    if (c?.next?.length === 0) {
                        c?.next?.push(deepClone(h))
                    } else {
                        build(deepClone(h), c)
                    }
                }
            });
            return children
        }
    }

}

export default Mdast
