package io.reflectoring.coderadar.projectadministration.service.project;

import static io.reflectoring.coderadar.projectadministration.service.project.CreateProjectService.getProjectDateRange;

import io.reflectoring.coderadar.CoderadarConfigurationProperties;
import io.reflectoring.coderadar.projectadministration.domain.Project;
import io.reflectoring.coderadar.projectadministration.port.driven.analyzer.UpdateCommitsPort;
import io.reflectoring.coderadar.projectadministration.port.driven.project.GetProjectPort;
import io.reflectoring.coderadar.projectadministration.port.driven.project.ListProjectsPort;
import io.reflectoring.coderadar.projectadministration.port.driven.project.ProjectStatusPort;
import io.reflectoring.coderadar.vcs.UnableToUpdateRepositoryException;
import io.reflectoring.coderadar.vcs.port.driver.GetProjectCommitsUseCase;
import io.reflectoring.coderadar.vcs.port.driver.UpdateRepositoryUseCase;
import java.nio.file.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;

@Component
public class ScanProjectScheduler {

  private final UpdateRepositoryUseCase updateRepositoryUseCase;
  private final CoderadarConfigurationProperties coderadarConfigurationProperties;
  private final GetProjectCommitsUseCase getProjectCommitsUseCase;
  private final UpdateCommitsPort updateCommitsPort;
  private final ListProjectsPort listProjectsPort;

  private final ProjectStatusPort projectStatusPort;
  private final TaskScheduler taskScheduler;
  private final GetProjectPort getProjectPort;

  private final Logger logger = LoggerFactory.getLogger(UpdateProjectService.class);

  @Autowired
  public ScanProjectScheduler(
      UpdateRepositoryUseCase updateRepositoryUseCase,
      CoderadarConfigurationProperties coderadarConfigurationProperties,
      GetProjectCommitsUseCase getProjectCommitsUseCase,
      UpdateCommitsPort updateCommitsPort,
      ListProjectsPort listProjectsPort,
      ProjectStatusPort projectStatusPort,
      TaskScheduler taskScheduler,
      GetProjectPort getProjectPort) {
    this.updateRepositoryUseCase = updateRepositoryUseCase;
    this.coderadarConfigurationProperties = coderadarConfigurationProperties;
    this.getProjectCommitsUseCase = getProjectCommitsUseCase;
    this.updateCommitsPort = updateCommitsPort;
    this.listProjectsPort = listProjectsPort;
    this.projectStatusPort = projectStatusPort;
    this.taskScheduler = taskScheduler;
    this.getProjectPort = getProjectPort;
  }

  @EventListener({ContextRefreshedEvent.class})
  public void onApplicationEvent() {
    for (Project project : listProjectsPort.getProjects()) {
      scheduleUpdateTask(project);
    }
  }

  /**
   * Schedules an update task for the project. This will do a pull on the repository and save the
   * commits in the database.
   *
   * @param project the project.
   */
  void scheduleUpdateTask(Project project) {
    taskScheduler.scheduleAtFixedRate(
        () -> {
          if (!projectStatusPort.isBeingProcessed(project.getId())) {
            try {
              Project currentProject = getProjectPort.get(project.getId());
              if (currentProject.getVcsEnd() != null) {
                return;
              }

              logger.info(
                  String.format("Scanning project %s for new commits!", currentProject.getName()));
              if (updateRepositoryUseCase.updateRepository(
                  Paths.get(
                      coderadarConfigurationProperties.getWorkdir()
                          + "/projects/"
                          + currentProject.getWorkdirName()))) {
                updateCommitsPort.updateCommits(
                    getProjectCommitsUseCase.getCommits(
                        Paths.get(currentProject.getWorkdirName()),
                        getProjectDateRange(currentProject)),
                    currentProject.getId());
              }
            } catch (UnableToUpdateRepositoryException e) {
              logger.error(String.format("Unable to update the project: %s", e.getMessage()));
            }
          }
        },
        coderadarConfigurationProperties.getScanIntervalInSeconds() * 1000);
  }
}

@@ -255,16 +255,97 @@
 erties;%0A
+import io.reflectoring.coderadar.projectadministration.ProjectNotFoundException;%0A
 import i
@@ -1029,16 +1029,110 @@
 .Paths;%0A
+import java.util.HashMap;%0Aimport java.util.Map;%0Aimport java.util.concurrent.ScheduledFuture;%0A%0A
 import o
@@ -2066,16 +2066,82 @@
 lass);%0A%0A
+  private Map%3CLong, ScheduledFuture%3C?%3E%3E tasks = new HashMap%3C%3E();%0A%0A
   @Autow
@@ -3409,16 +3409,43 @@
 t) %7B%0A   
+ tasks.put(project.getId(),
  taskSch
@@ -3488,16 +3488,34 @@
 () -%3E %7B%0A
+          try %7B%0A  
         
@@ -3572,24 +3572,26 @@
 getId())) %7B%0A
+  
             
@@ -3606,24 +3606,26 @@
             
+  
 Project curr
@@ -3666,32 +3666,34 @@
 oject.getId());%0A
+  
               if
@@ -3748,16 +3748,18 @@
         
+  
 return;%0A
@@ -3772,19 +3772,22 @@
         
-%7D%0A%0A
+  %7D%0A  
         
@@ -3805,16 +3805,22 @@
 r.info(%0A
+      
         
@@ -3921,24 +3921,26 @@
             
+  
 if (updateRe
@@ -3983,32 +3983,38 @@
                 
+  
+    
 Paths.get(%0A     
@@ -4008,16 +4008,26 @@
 hs.get(%0A
+          
         
@@ -4112,16 +4112,30 @@
         
+              
 + %22/proj
@@ -4167,16 +4167,30 @@
         
+      
+        
 + curren
@@ -4236,16 +4236,18 @@
         
+ 
+ 
 updateCo
@@ -4263,32 +4263,38 @@
 .updateCommits(%0A
+      
                 
@@ -4350,32 +4350,42 @@
                 
+ 
+         
 Paths.get(curren
@@ -4428,32 +4428,42 @@
                 
+       
+   
 getProjectDateRa
@@ -4480,24 +4480,30 @@
 tProject)),%0A
+      
             
@@ -4541,34 +4541,38 @@
 ;%0A              
-%7D%0A
+  %7D%0A  
             %7D ca
@@ -4611,24 +4611,26 @@
 eption e) %7B%0A
+  
             
@@ -4708,24 +4708,26 @@
 essage()));%0A
+  
             
@@ -4720,32 +4720,148 @@
               %7D%0A
+            %7D%0A          %7D catch (ProjectNotFoundException e)%7B%0A            tasks.get(project.getId()).cancel(false);%0A
           %7D%0A    
@@ -4942,16 +4942,17 @@
  * 1000)
+)
 ;%0A  %7D%0A%7D%0A

stop scan job when the project is deleted