
from patch.include import *
from tqdm import tqdm


def _calc_perm_parent(self):
    perm_parent = None
    if self.tree_parent and not isinstance(self, (models.CmfComment, models.CmfTask)):
        perm_parent = self.tree_parent
    elif self.parent:
        perm_parent = self.parent
    self.perm_parent = perm_parent


def patch_one():
    # Считаем, что ACL в системе нет и надо только построить иерархию.
    total_count = 0
    total_all_count = 0
    for model_cls in cmf.models.CmfEntity.iter_subclasses():
        if model_cls.class_name in ('CmfAccessList', 'CmfAccessRule'):
            continue
        model_count = 0
        model_all_count = 0
        print(f'Patch {model_cls.class_name}')
        limit = 1000
        start = 0
        cnt = model_cls.count(
                filter=[
                    [
                        'OR',
                        ['parent_id', '!=', None],
                        ['tree_parent_id', '!=', None],
                    ],
                ],
                fields=['parent', 'tree_parent', 'perm_parent'])
        print(f'CNT: {cnt}')
        while True:
            obj_list = model_cls.list(
                filter=[
                    [
                        'OR',
                        ['parent_id', '!=', None],
                        ['tree_parent_id', '!=', None],
                    ],
                ],
                fields=['parent', 'tree_parent', 'perm_parent'],
                slice=[start, start+limit]
            )
            start += limit

            if not obj_list:
                break

            for obj in tqdm(obj_list):
                model_all_count += 1
                # print(f'{obj}, {obj.parent}, {obj.tree_parent}, {obj.perm_parent}')
                if (obj.parent or obj.tree_parent) and not obj.perm_parent:
                    _calc_perm_parent(obj)
                    obj.save(only_data=True)
                    model_count += 1
                    if model_count % 1000 == 0:
                        commit_all_ds()  # большие транзакции замедляются
            # else:
            #     print('!else')
            commit_all_ds()
            print(f'  obj count {model_count}/{model_all_count}')
            total_count += model_count
            total_all_count += model_all_count
    print(f'Total count {total_count}/{total_all_count}')


def patch_two():
    from common.models.cmf_active_entity import CmfActiveEntity
    set_count = 0

    def set_object_policy(obj_id_, policy_anonymous_):
        nonlocal set_count
        set_count += 1
        obj_fields = cmfutil.get_model_by_id(obj_id_)().save_preload_fields()
        obj = cmfutil.get_obj_by_id(obj_id_, fields=obj_fields)  # type: CmfActiveEntity
        # будем ломать прогресбар
        print(f'Set {obj} anonymous policy to {policy_anonymous_}')
        obj.perm_policy_anonymous = policy_anonymous_
        obj.save()  # может быть тяжёлым.

    ae_dict = {}
    print('Load data')
    for model in tqdm(CmfActiveEntity.iter_subclasses()):
        for obj_data in model.slist(
                # filter=['is_public', '=', True],
                fields=['--', 'id', 'perm_parent_id', 'is_public*', 'perm_policy_anonymous']):
            ae_dict[obj_data.id] = obj_data

    print('Process data')
    for obj_id, obj_data in tqdm(ae_dict.items()):
        if obj_data.perm_policy_anonymous != 'default':
            continue
        parent_data = ae_dict.get(obj_data.perm_parent_id)
        if not obj_data.is_public:
            # Если paren.is_public, то ставим политику deny
            if parent_data and parent_data.is_public:
                set_object_policy(obj_id, 'deny')
        else:
            if parent_data \
                    and (obj_data.is_public, obj_data.is_public_editable, obj_data.is_public_comments) \
                    == (parent_data.is_public, parent_data.is_public_editable, parent_data.is_public_comments):
                continue  # use default inheritance
            policy_anonymous = 'read'
            if obj_data.is_public_editable:
                policy_anonymous = 'full'
            elif obj_data.is_public_comments:
                policy_anonymous = 'comment'
            set_object_policy(obj_id, policy_anonymous)
    print(f'Patched {set_count}/{len(ae_dict)}')


@app_context(commit=True)
def patch():
    """
    Для тестирования патча: ( cd /opt/crm; python3 -m patch.20220XXXXXXX_PATCHNAME )
    Здесь можно работать с моделями через models.CmfTask и т.д.
    Для прогрессбара используйте:
    for task in tqdm(models.CmfTask.list()):
        ...
    """
    patch_one()
    patch_two()


if __name__ == "__main__":
    patch()
