from patch.include import *
from tqdm import tqdm


# Пришлось перенести метод в патч, т.к. в оригинальном есть проверка на изменение дат, и пересчет длительности не отрабатывает
def from_sched_dates_of_child_tasks(parent_task, child_task=None, ignore_child_task=False, path=None, deferred=False):
    if not child_task:
        ignore_child_task = True

    if parent_task.const_duration:
        return

    if path is None:
        path = set()
    if parent_task.id.value in path:
        return
    path.add(parent_task.id.value)

    min_start_date = None
    max_finish_dates = None
    child_task_with_min_start = child_task if not ignore_child_task else None
    child_task_with_max_finish = child_task if not ignore_child_task else None

    add_filter = [
        'OR',
        ['sched_start_date', '!=', None],
        ['sched_finish_date', '!=', None],
    ]
    gantt_tasks = parent_task._get_children_gantt_tasks(add_filter=add_filter)

    for gantt_task in gantt_tasks:
        if child_task and child_task.id == gantt_task.id:
            continue

        if gantt_task.sched_start_date:
            if not child_task_with_min_start:
                child_task_with_min_start = gantt_task
            else:
                if not child_task_with_min_start.sched_start_date:
                    child_task_with_min_start = gantt_task
                elif child_task_with_min_start.sched_start_date > gantt_task.sched_start_date:
                    child_task_with_min_start = gantt_task

        if gantt_task.sched_finish_date:
            if not child_task_with_max_finish:
                child_task_with_max_finish = gantt_task
            else:
                if not child_task_with_max_finish.sched_finish_date:
                    child_task_with_max_finish = gantt_task
                elif child_task_with_max_finish.sched_finish_date < gantt_task.sched_finish_date:
                    child_task_with_max_finish = gantt_task

    if child_task_with_min_start:
        min_start_date = child_task_with_min_start.sched_start_date
    if child_task_with_max_finish:
        max_finish_dates = child_task_with_max_finish.sched_finish_date

    if (not ignore_child_task and min_start_date
            and child_task.sched_finish_date and not child_task.sched_start_date
            and child_task.sched_finish_date < min_start_date):
        min_start_date = child_task.sched_finish_date

    if (not ignore_child_task and max_finish_dates
            and child_task.sched_start_date and not child_task.sched_finish_date
            and child_task.sched_start_date > max_finish_dates):
        max_finish_dates = child_task.sched_start_date

    constraint = None

    conflict_child_task_min_start = None
    if parent_task.constrain_start_date and min_start_date:
        conflict_child_task_min_start = (f'"{child_task_with_min_start.task.name}"'
                                            f' ({child_task_with_min_start.task.code})')
        constrain_start_date = parent_task.constrain_start_date.value
        if (parent_task.constrain_start_type == '0-const'
                and min_start_date != constrain_start_date):
            constraint = f'Фиксированное начало {constrain_start_date:%d.%m.%Y}'
        elif (parent_task.constrain_start_type == '3-after'
                and min_start_date < constrain_start_date):
            constraint = f'Начало не раньше {constrain_start_date:%d.%m.%Y}'
        elif (parent_task.constrain_start_type == '4-before'
                and min_start_date > constrain_start_date):
            constraint = f'Начало не позже {constrain_start_date:%d.%m.%Y}'
        else:
            conflict_child_task_min_start = None

    conflict_child_task_max_finish = None
    if parent_task.constrain_finish_date and max_finish_dates:
        conflict_child_task_max_finish = (f'"{child_task_with_max_finish.task.name}"'
                                            f' ({child_task_with_max_finish.task.code})')
        constrain_finish_date = parent_task.constrain_finish_date.value
        if (parent_task.constrain_finish_type == '0-const'
                and max_finish_dates != constrain_finish_date):
            constraint = f'Фиксированное окончание {constrain_finish_date:%d.%m.%Y}'
        elif (parent_task.constrain_finish_type == '1-after'
                and max_finish_dates < constrain_finish_date):
            constraint = f'Окончание не раньше {constrain_finish_date:%d.%m.%Y}'
        elif (parent_task.constrain_finish_type == '2-before'
                and max_finish_dates > constrain_finish_date):
            constraint = f'Окончание не позже {constrain_finish_date:%d.%m.%Y}'
        else:
            conflict_child_task_max_finish = None

    if constraint:
        conflict_child_task = conflict_child_task_min_start or conflict_child_task_max_finish
        cmf_alert(f'Конфликт планирования: В родительской задаче'
                    f' "{parent_task.task.name}" ({parent_task.task.code}) ограничение "{constraint}"'
                    f' конфликтует с дочерней задачей {conflict_child_task}.', abort=True)

    parent_task.sched_start_date = min_start_date
    parent_task.sched_finish_date = max_finish_dates

    parent_task._calc_sched_duration(from_child_changes=True)

    parent_gantt_tasks = parent_task._get_parent_gantt_tasks()
    if parent_gantt_tasks:
        with cmfutil.disable_acl():
            for parent_gantt_task in parent_gantt_tasks:
                parent_gantt_task.load_fields(parent_task.save_preload_fields())
                from_sched_dates_of_child_tasks(
                    parent_task=parent_gantt_task,
                    child_task=parent_task,
                    ignore_child_task=ignore_child_task,
                    path=path
                )
                parent_gantt_task.save(notify=False, emit=False, only_data=True)


@app_context(commit=True)
def fix_summary_task_duration():
    """
    Для тестирования патча: ( cd /opt/eva-app; python3 -m patch.202407111402_fix_summary_task_duration )
    BCRM ~ 1.5 мин
    """
    print('Запуск патча fix_summary_task_duration')
    fields = [
        '*',
        'parent',
        'task',
        'gantt_project',
        'gantt_project.op_gantt_task',
        'parent_task',
        'parent_task.gantt_project',
        'parent_task.op_gantt_task',
        'task.logic_prefix',
        'task.parent_task.logic_prefix',
        'task.has_child_tasks',
        'task.plan_start_date',
        'task.plan_end_date',
        'task.responsible',
        'task.executors',
    ]

    # Сначала фиксим родителей по связям доп.родитель, доп.дочерняя
    additional_parents_relations = models.CmfRelationOption.list(filter=['relation_type.code', '=', 'system.additional_parent'],
                                                                 fields=['in_link.op_gantt_task', 'out_link.op_gantt_task'])
    if additional_parents_relations:
        additional_parents_with_children = [(rel.in_link.op_gantt_task, rel.out_link.op_gantt_task) for rel in additional_parents_relations]

        for parent_gantt_task, child_gantt_task in additional_parents_with_children:
            parent_gantt_task.load_fields(fields)
            child_gantt_task.load_fields(fields)
            if child_gantt_task.sched_start_date or child_gantt_task.sched_finish_date:
                try:
                    from_sched_dates_of_child_tasks(parent_gantt_task, child_gantt_task)
                except Exception as e:
                    print(f'Неудалось изменить даты задачи {parent_gantt_task.task.name}: {e}')
                    continue
                parent_gantt_task.save(only_data=True, notify=False, emit=False)
            commit_all_ds()

    # Потом фиксим родительские задачи у которых исполнителей и соисполнителей вы сумме больше чем 1
    summury_filter = [['task.has_child_tasks', '==', True], 
                      ['OR', ['sched_start_date', '!=', None], ['sched_finish_date', '!=', None]],
                      ['AND', ['sched_duration', '!=', 0], ['sched_duration', '!=', None]]]

    i = 0
    while True:
        parent_gantt_tasks = models.CmfGanttTask.list(filter=summury_filter, slice=[i, i+100], order_by=['-cmf_created_at'],
                                                      fields=fields + ['task.executors', 'task.responsible'])

        if not parent_gantt_tasks:
            break

        for parent_gantt_task in parent_gantt_tasks:
            executors_len = len(parent_gantt_task.task.executors.value)
            if parent_gantt_task.task.responsible:
                executors_len += 1
            if executors_len < 2:
                continue

            try:
                from_sched_dates_of_child_tasks(parent_gantt_task)
            except Exception as e:
                print(f'Неудалось изменить даты задачи {parent_gantt_task.task.name}: {e}')
                continue
            parent_gantt_task.save(only_data=True)

        commit_all_ds()
        i += 100


if __name__ == "__main__":
    fix_summary_task_duration()
