from genericpath import exists
import json
import os
import uuid
import sys
import re

id_set = set()


def load_json(p):
    with open(p, 'r') as f:
        return json.loads(f.read())


def dump_json(p, j, exist_ok=False, override=False):
    if os.path.exists(p):
        if exist_ok:
            if not override:
                return
        else:
            print(f"{p} already exist")
            sys.exit(0)

    with open(p, 'w+') as f:
        f.write(json.dumps(j, indent=2, ensure_ascii=False))


def parse_no_name(d):
    p = r'(\d+)\.(.*)'
    m = re.search(p, d)

    try:
        no = int(m.group(1))
        dir_name = m.group(2)
    except:
        sys.exit(0)

    return no, dir_name


def check_export(base, cfg):
    flag = False
    exports = []
    for export in cfg.get('export', []):
        ecfg_path = os.path.join(base, export)
        if os.path.exists(ecfg_path):
            exports.append(export)
        else:
            flag = True
    if flag:
        cfg["export"] = exports
    return flag


def gen_tree(data_path):
    root = {}

    def gen_node_id():
        # return ''.join(str(uuid.uuid5(uuid.NAMESPACE_URL, 'skill_tree')).split('-'))
        return "algorithm-" + uuid.uuid4().hex

    def list_dir(p):
        v = os.listdir(p)
        v.sort()
        for no_name in v:
            no_dir = os.path.join(p, no_name)
            if os.path.isdir(no_dir):
                yield no_dir, no_name

    def ensure_id_helper(node):
        flag = False

        if (node.get('node_id') is None) or node.get('node_id') in id_set:
            node['node_id'] = gen_node_id()
            flag = True

        id_set.add(node['node_id'])

        if 'children' in node:
            for c in node["children"]:
                flag = flag or ensure_id_helper(list(c.values())[0])

        return flag

    def ensure_node_id(cfg):
        return ensure_id_helper(cfg)

    def ensure_title_helper(node, cfg_path, title=""):
        flag = False

        if node.get('title') is None:
            if cfg_path:
                node['title'] = re.sub(
                    "^[0-9]{1,3}\.", "", os.path.split(os.path.dirname(cfg_path))[-1])
            else:
                node['title'] = title
            flag = True

        if 'children' in node:
            for c in node["children"]:
                flag = flag or ensure_title_helper(
                    list(c.values())[0], None, list(c.keys())[0])

        return flag

    def ensure_title(cfg, cfg_path):
        return ensure_title_helper(cfg, cfg_path)

    def make_node(name, node_id, keywords, children=None):
        node = {}
        node_children = children or []
        node[name] = {
            'node_id': node_id,
            'keywords': keywords,
            'children': node_children
        }
        return node, node_children

    # 根节点
    cfg_path = os.path.join(data_path, 'config.json')
    cfg = load_json(cfg_path)
    if ensure_node_id(cfg):
        dump_json(cfg_path, cfg, exist_ok=True, override=True)

    if ensure_title(cfg, cfg_path):
        cfg["title"] = "C"
        dump_json(cfg_path, cfg, exist_ok=True, override=True)
    tree_node = {
        "node_id": cfg['node_id'],
        "keywords": cfg['keywords'],
        "children": []
    }
    root[cfg['tree_name']] = tree_node

    # 难度节点
    for level_no_dir, level_no_name in list_dir(data_path):
        print(level_no_dir)
        no, level_name = parse_no_name(level_no_name)
        level_path = os.path.join(level_no_dir, 'config.json')
        level_cfg = load_json(level_path)
        if ensure_node_id(level_cfg) or check_export(level_no_dir, level_cfg):
            dump_json(level_path, level_cfg, exist_ok=True, override=True)
        if ensure_title(level_cfg, level_path):
            dump_json(level_path, level_cfg, exist_ok=True, override=True)

        level_node, level_node_children = make_node(
            level_name, level_cfg['node_id'], level_cfg['keywords'])
        tree_node['children'].append(level_node)

        # 章节点
        for chapter_no_dir, chapter_no_name in list_dir(level_no_dir):
            no, chapter_name = parse_no_name(chapter_no_name)
            chapter_path = os.path.join(chapter_no_dir, 'config.json')
            chapter_cfg = load_json(chapter_path)
            if ensure_node_id(chapter_cfg) or check_export(chapter_no_dir, chapter_cfg):
                dump_json(chapter_path, chapter_cfg,
                          exist_ok=True, override=True)
            if ensure_title(chapter_cfg, chapter_path):
                dump_json(chapter_path, chapter_cfg,
                          exist_ok=True, override=True)

            chapter_node, chapter_node_children = make_node(
                chapter_name, chapter_cfg['node_id'], chapter_cfg['keywords'])
            level_node_children.append(chapter_node)

            # 知识点
            for section_no_dir, section_no_name in list_dir(chapter_no_dir):
                no, section_name = parse_no_name(section_no_name)
                sec_path = os.path.join(section_no_dir, 'config.json')
                sec_cfg = load_json(sec_path)
                flag = ensure_node_id(sec_cfg) or check_export(
                    section_no_dir, sec_cfg)

                section_node, section_node_children = make_node(
                    section_name, sec_cfg['node_id'], sec_cfg['keywords'], sec_cfg.get('children', []))
                chapter_node_children.append(section_node)

                # 确保习题分配了习题ID

                for export in sec_cfg.get("export", []):
                    ecfg_path = os.path.join(section_no_dir, export)
                    ecfg = load_json(ecfg_path)

                    if (ecfg.get('exercise_id') is None) or (ecfg.get('exercise_id') in id_set):
                        ecfg['exercise_id'] = uuid.uuid4().hex
                        dump_json(ecfg_path, ecfg,
                                  exist_ok=True, override=True)

                    id_set.add(ecfg['exercise_id'])

                if flag:
                    dump_json(sec_path, sec_cfg, exist_ok=True, override=True)

                if ensure_title(sec_cfg, sec_path):
                    dump_json(sec_path, sec_cfg, exist_ok=True, override=True)

    # 保存技能树骨架
    tree_path = os.path.join(data_path, 'tree.json')
    dump_json(tree_path, root, exist_ok=True, override=True)
