class LaunchingRepositoryBuildVisitor extends AbstractRepositoryBuildVisitor {

  @Override
  protected void visitLaunching(RepositoryBuild build) throws Exception {
    LOG.info("Going to enqueue module builds for repository build {}", build.getId().get());

    final Set<Module> activeModules = filterActive(moduleService.getByBranch(build.getBranchId()));

    // 1. Check for malformed files
    if (!malformedFileService.getMalformedFiles(build.getBranchId()).isEmpty()) {
      failBranchAndModuleBuilds(build, activeModules);
      return;
    }

    // 2. Check for buildable modules
    if (activeModules.isEmpty()) {
      LOG.info("No active modules to build in branch {} - failing build", build.getId().get());
      repositoryBuildService.fail(build);
      return;
    }

    final Optional<Long> interProjectBuildId;
    final Set<Module> toBuild;

    // 3. The modules we choose to build depends on if this is an InterProject build or not
    //    If this is an InterProject build we enqueue one of those as well.
    if (build.getBuildOptions().getBuildDownstreams() == BuildDownstreams.INTER_PROJECT) {
      toBuild = determineModulesToBuildUsingInterProjectBuildGraph(build, activeModules);
      InterProjectBuild ipb = InterProjectBuild.getQueuedBuild(ImmutableSet.copyOf(getIds(toBuild)), build.getBuildTrigger());
      interProjectBuildId = Optional.of(interProjectBuildService.enqueue(ipb));
    } else {
      interProjectBuildId = Optional.absent();
      toBuild = findModulesToBuild(build, activeModules);
    }

    // 4. Launch the modules we want to build
    for (Module module : build.getDependencyGraph().get().orderByTopologicalSort(toBuild)) {

      enqueueModuleBuild(build, module);
      if (build.getBuildOptions().getBuildDownstreams() == BuildDownstreams.INTER_PROJECT) {
        interProjectBuildMappingService.insert(InterProjectBuildMapping.makeNewMapping(interProjectBuildId.get(), build.getBranchId(), build.getId(), module.getId().get()));
      }
    }

    // 5. Only calculate skipped modules after we know what modules will build
    Set<Module> skipped = Sets.difference(activeModules, toBuild);
    for (Module module : skipped) {
      moduleBuildService.skip(build, module);
    }

    // 6. Update the state of this branch build.
    repositoryBuildService.update(build.toBuilder().setState(State.IN_PROGRESS).build());
  }

}

class LaunchingRepositoryBuildVisitorTest {

  private static final BranchService branchService;
  private static final BuildConfigUtils buildConfigUtils;
  private static final RepositoryBuildService repositoryBuildService;
  private static final ModuleBuildService moduleBuildService;
  private static final MalformedFileService malformedFileService;
  private static final InterProjectBuildService interProjectBuildService;
  private static final InterProjectBuildMappingService interProjectBuildMappingService;
  private static final ModuleService moduleService;
  private static final DependenciesService dependenciesService;
  private static final GitHubHelper gitHubHelper;
  private static final GitInfo branch;
  private static final BuildConfig defaultBuildConfig;
  private static final Module activeModule;
  private static final Module inactiveModule;
  private static final Map<Integer, Set<Integer>> dependencyMap;
  private static final CommitInfo commitInfo;
  private static final RepositoryBuild launchingBuild;
  private static final LaunchingRepositoryBuildVisitor buildVisitor;

  @Test
  public void itEnqueuesModuleBuildsAndUpdatesRepositoryBuildToLaunching() throws Exception {

    when(malformedFileService.getMalformedFiles(anyInt())).thenReturn(ImmutableSet.of());
    when(moduleService.getByBranch(1)).thenReturn(ImmutableSet.of(activeModule, inactiveModule));

    doThrow(new RuntimeException("Build should not have been failed")).when(repositoryBuildService).fail(any());

    RepositoryBuild[] savedRepositoryBuild = {null};
    doAnswer(invocation -> {
      savedRepositoryBuild[0] = (RepositoryBuild) invocation.getArguments()[0];
      return null;
    }).when(repositoryBuildService).update(anyObject());

    List<Module> modulesThatWereEnqueued = new ArrayList<>();
    doAnswer(invocation -> {
      modulesThatWereEnqueued.add((Module) invocation.getArguments()[1]);
      return null;
    }).when(moduleBuildService).enqueue(anyObject(), anyObject(), anyObject(), anyObject());

    RepositoryBuild expectedRepositoryBuild = launchingBuild.toBuilder().setState(RepositoryBuild.State.IN_PROGRESS).build();

    buildVisitor.visitLaunching(launchingBuild);

    assertThat(savedRepositoryBuild).isNotNull();
    assertThat(savedRepositoryBuild[0]).isEqualTo(expectedRepositoryBuild);

    assertThat(modulesThatWereEnqueued).doesNotContain(inactiveModule);
    assertThat(modulesThatWereEnqueued).isEqualTo(ImmutableList.of(activeModule));
  }

}