package io.openex.scheduler.jobs;

import io.openex.database.model.*;
import io.openex.database.repository.DryInjectRepository;
import io.openex.database.repository.ExerciseRepository;
import io.openex.database.repository.InjectRepository;
import io.openex.execution.ExecutableInject;
import io.openex.execution.Execution;
import io.openex.execution.Executor;
import io.openex.helper.InjectHelper;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

import static java.time.Instant.now;
import static java.util.stream.Collectors.groupingBy;

@Component
@DisallowConcurrentExecution
public class InjectsExecutionJob implements Job {

    private ApplicationContext context;
    private InjectHelper injectHelper;
    private DryInjectRepository dryInjectRepository;
    private InjectRepository injectRepository;
    private ExerciseRepository exerciseRepository;

    @Autowired
    public void setInjectRepository(InjectRepository injectRepository) {
        this.injectRepository = injectRepository;
    }

    @Autowired
    public void setDryInjectRepository(DryInjectRepository dryInjectRepository) {
        this.dryInjectRepository = dryInjectRepository;
    }

    @Autowired
    public void setExerciseRepository(ExerciseRepository exerciseRepository) {
        this.exerciseRepository = exerciseRepository;
    }

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setInjectHelper(InjectHelper injectHelper) {
        this.injectHelper = injectHelper;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            // Handle starting exercises if needed.
            List<Exercise> exercises = exerciseRepository.findAllShouldBeInRunningState(now());
            exercises.stream().parallel().forEach(exercise -> {
                exercise.setStatus(Exercise.STATUS.RUNNING);
                exerciseRepository.save(exercise);
            });
            List<ExecutableInject<?>> injects = injectHelper.getInjectsToRun();
            // Get all injects to execute grouped by exercise.
            Map<String, List<ExecutableInject<?>>> byExercises = injects.stream()
                    .collect(groupingBy(ex -> ex.getInject().getExercise().getId()));
            // Execute injects in parallel for each exercise.
            byExercises.values().stream().parallel().forEach(executableInjects -> {
                // Execute each inject for the exercise in order.
                executableInjects.forEach(executableInject -> {
                    Inject inject = executableInject.getInject();
                    Injection source = executableInject.getSource();
                    Class<? extends Executor<?>> executorClass = inject.executor();
                    Executor<? extends Inject> executor = context.getBean(executorClass);
                    Execution execution = executor.executeInRange(executableInject);
                    // Report inject execution
                    if (source instanceof Inject) {
                        Inject executedInject = injectRepository.findById(source.getId()).orElseThrow();
                        executedInject.setStatus(InjectStatus.fromExecution(execution, executedInject));
                        injectRepository.save(executedInject);
                    }
                    // Report dry inject execution
                    if (source instanceof DryInject) {
                        DryInject executedDry = dryInjectRepository.findById(source.getId()).orElseThrow();
                        executedDry.setStatus(DryInjectStatus.fromExecution(execution, executedDry));
                        dryInjectRepository.save(executedDry);
                    }
                });
            });
            // Change status of finished exercises.
            List<Exercise> mustBeFinishedExercises = exerciseRepository.thatMustBeFinished();
            exerciseRepository.saveAll(mustBeFinishedExercises.stream()
                    .peek(exercise -> exercise.setStatus(Exercise.STATUS.FINISHED)).toList());
        } catch (Exception e) {
            throw new JobExecutionException(e);
        }
    }
}

@@ -2262,16 +2262,62 @@
 NNING);%0A
+                exercise.setUpdatedAt(now());%0A
         
@@ -2538,14 +2538,16 @@
 Map%3C
-String
+Exercise
 , Li
@@ -2676,24 +2676,16 @@
 ercise()
-.getId()
 ));%0A    
@@ -2770,14 +2770,16 @@
 ses.
-values
+entrySet
 ().s
@@ -2810,29 +2810,149 @@
 ch(e
-xecutableInjects -%3E %7B
+ntry -%3E %7B%0A                Exercise exercise = entry.getKey();%0A                List%3CExecutableInject%3C?%3E%3E executableInjects = entry.getValue();
 %0A   
@@ -4279,32 +4279,168 @@
             %7D);%0A
+                // Update the exercise%0A                exercise.setUpdatedAt(now());%0A                exerciseRepository.save(exercise);%0A
             %7D);%0A
@@ -4431,32 +4431,32 @@
             %7D);%0A
-
             // C
@@ -4694,16 +4694,42 @@
 rcise -%3E
+ %7B%0A                       
  exercis
@@ -4765,16 +4765,93 @@
 INISHED)
+;%0A                        exercise.setUpdatedAt(now());%0A                    %7D
 ).toList

Update updated at of exercise in inject job