import { unified } from 'unified';
import parse from 'remark-parse';
import { visit } from 'unist-util-visit';

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: any) => {
    const re = /(\d{2}):(\d{2}):(\d{2})\.(\d{3})/;
    if (re.test(str)) {
        const match = str.match(re);
        const h = match[1] ? Number(match[1]) * 3600 : 0;
        const m = match[2] ? Number(match[2]) * 60 : 0;
        const s = match[3] ? Number(match[3]) : 0;
        const ms = match[3] ? Number(match[4]) / 1000 : 0;
        return h + m + s + ms;
    }
    return null;
};

const format = (text: any) => {
    let content = text || '';
    const t = /\`\{\{(.*?)\}\}/;
    let e: any;
    while ((e = t.exec(content))) {
        content = content.replace(t, '`\n' + `#${e[0].substring(1)}\n`);
    }
    return content;
};

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

const linkToText = (): any => {
    return (tree: any) => {
        // console.log('code =>', deepClone(tree));
        visit(tree, 'link', (node: any, index: any, parent: any) => {
            console.debug(node, index, parent);
            console.log(node)
            node.type = 'text'
            node.value = node.children[0].value
            delete node.children
        });
        return tree;
    };
}

const code = (): any => {
    return (tree: any) => {
        // console.log('code =>', deepClone(tree));
        visit(tree, 'paragraph', (node: any, index: any, parent: any) => {
            console.debug(node, index, parent);
            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) => {
        // console.log('inlineCode =>', deepClone(tree));
        visit(tree, 'inlineCode', (node: any, index: any, parent: any) => {
            console.debug(node, index, parent);
            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 tag = (): any => {
    return (tree: any) => {
        console.log('taging =>', deepClone(tree));
        visit(tree, 'paragraph', (node: any, index: any, parent: any) => {
            console.debug(node, index, parent)
            node.children.forEach((child: any) => {
                const re = /(#[doctiwer]\s\S+)/g;
                const str = child.value
                if ('text' === child.type && re.test(str)) {
                    const array = str.split(re).filter((i: any) => i.trim())
                    const data: any = []
                    array.forEach((line: any) => {
                        if (/#([doctiwer])\s(\S+)/.test(line)) {
                            const match = line.match(/#([doctiwer])\s(\S+)/);
                            const el = {
                                type: match[1],
                                value: match[2],
                            };
                            data.push(el);
                        } else {
                            let type = 'text';
                            let value = line.trim();
                            const el = {
                                type: type,
                                value: value,
                            };
                            data.push(el);
                        }
                    });
                    child.type = 'paragraph'
                    child.children = data
                    delete child.value
                }
            });
        });
        return tree;
    };
}

const subtitle = (): any => {
    return (tree: any) => {
        // console.log('subtitle =>', deepClone(tree));
        visit(tree, 'text', (node: any, index: any, parent: any) => {
            if ('paragraph' !== parent.type) {
                return;
            }
            // console.log(node)
            console.debug(node, index, parent);
            const re = /\[(\d{2}:\d{2}:\d{2}\.\d{3})(?:-(\d{2}:\d{2}:\d{2}\.\d{3}))?\](.*)/i;
            const str = node.value.trim();
            if (re.test(str)) {
                const values = str.match(re);
                node.type = 'subtitle';
                node.properties = {
                    st: values[1],
                    et: values[2],
                };
                node.value = values[3];
            }
        });
        return tree;
    };
};

const paragraph = (): any => {
    return (tree: any) => {
        // console.log('refresh =>', deepClone(tree));
        visit(tree, 'paragraph', (node: any, index: any, parent: any) => {
            if ('paragraph' !== parent.type) {
                return
            }
            const children = node.children
            parent.children.splice(index, 1, ...children)
            console.debug(node, index, parent);
        });
        return tree;
    };
};

const Parser = {
    mdast: (text?: string) => {
        const content = format(text || '');
        const processer = unified()
            .use(parse)
            .use(linkToText)
            .use(code)
            .use(inlineCode)
            .use(tag)
            .use(subtitle)
            .use(paragraph);
        const tree = processer.parse(content);
        const ast = processer.runSync(tree);
        processer.freeze();
        console.log('ast =>', ast);
        return deepClone(ast);
    },

    subtitle: (mdast: any) => {
        const data: any = [];
        const processer = unified().use(() => (tree: any) => {
            visit(tree, 'subtitle', (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 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: timer(ext.substring(7).trim()),
                            et: undefined,
                            value: node.value.trim()
                        }
                        data.push(item)
                    }
                })
            })
        processer.runSync(mdast)
        processer.freeze()
        return data
    }
}
export default Parser;
