from collections import defaultdict

from patch.include import *
from tqdm import tqdm
from cmf.system_data import scaffold_project_role, scaffold_project_perm
from cmf.models import CmfEntity


@app_context(commit=True)
def patch():
    with cmfutil.disable_acl(), cmfutil.disable_notify():
        g.debug('Build Relation cache', force=True)
        parents = dict()
        skip_models = (
                # Acl пока не удаляем
                cmf.include.models.CmfAccessList,
                cmf.include.models.CmfAccessRule,
                # Comment для оптимизации обработаем отдельно
                cmf.include.models.CmfComment,
            )
        for model in CmfEntity.iter_subclasses():
            if model in skip_models:
                continue
            for obj_data in model.slist(fields=['--', 'id', 'parent_id'], TECHCOM_nocache=True, include_templates=True):
                if getattr(obj_data, 'parent_id', None):
                    parents[sys.intern(obj_data.id)] = sys.intern(obj_data.parent_id)

        def calc_root_parent_id(obj_id: str):
            path = []  # чтобы порядок сохранить
            root = None
            while obj_id:
                root = obj_id
                obj_id = parents.get(obj_id)
                if obj_id in path:
                    g.debug(f'Relation loop: {path} -> {obj_id}')
                    break
                path.append(obj_id)
            return root

        for model in CmfEntity.iter_subclasses():
            if model in skip_models:
                continue
            g.debug(f'Process Model {model.class_name}', force=True)
            progress = tqdm(total=model.count(filter=['root_parent_id', '=', None], include_templates=True))
            fields = model(empty=True).save_preload_fields()
            while progress.total and (obj_list := model.list(
                    fields=fields, filter=['root_parent_id', '=', None], include_templates=True, slice=[0, 1000])):
                for obj in obj_list:
                    root_parent_id = calc_root_parent_id(obj.id.value)
                    try:
                        root_parent = root_parent_id and cmfutil.get_obj_by_id(root_parent_id) or obj
                    except KeyError:
                        g.debug(f'invalid id {root_parent_id}')
                        root_parent = obj
                    obj.root_parent = root_parent or obj
                    obj.save(only_data=True, emit=False)
                commit_all_ds()
                progress.update(len(obj_list))
                if progress.n > progress.total:
                    print('!!! Infinity loop')
                    break
            progress.close()

        model = models.CmfComment
        g.debug(f'Process Model {model.class_name}', force=True)
        # progress = tqdm(total=model.count(filter=[['root_parent_id', '=', None], ['parent_id', '!=', None]]))
        # fields = model(empty=True).save_preload_fields()
        # while obj_list := model.list(
        #         fields=fields, filter=[['root_parent_id', '=', None], ['parent_id', '!=', None]], slice=[0, 1000]):
        #     for obj in obj_list:
        #         root_parent_id = calc_root_parent_id(obj.parent_id.value)
        #         try:
        #             root_parent = root_parent_id and cmfutil.get_obj_by_id(root_parent_id) or obj.parent
        #         except KeyError:
        #             g.debug(f'invalid id {root_parent_id}')
        #             root_parent = obj.parent
        #         obj.root_parent = root_parent or obj.parent
        #         obj.save(only_data=True, emit=False)
        #     commit_all_ds()
        #     progress.update(len(obj_list))
        #     if progress.n > progress.total:
        #         print('!!! Infinity loop')
        #         break
        # progress.close()

        for model in tqdm(list(CmfEntity.iter_subclasses())):
            if model in skip_models:
                continue
            result = model.dp.data_driver.Session().execute(f'''
update cmf_comment c
    set root_parent_id = (select root_parent_id from {model.tablename} t where t.id = c.parent_id)
    where root_parent_id is null and parent_id is not null and parent_id LIKE '{model.class_name}:%';''')
            commit_all_ds()
            if result.rowcount:
                g.debug(f'{model.class_name}: updated rows {result.rowcount}')
        g.debug(f'finish', force=True)


if __name__ == "__main__":
    patch()
