// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.acceptance.pgm;

import static com.google.common.truth.Truth8.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
import com.google.gerrit.entities.Project;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.project.ProjectData;
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jgit.util.FS;
import org.junit.Test;

@NoHttpd
public class InitIT extends StandaloneSiteTest {

  @Test
  public void indexesAllProjectsAndAllUsers() throws Exception {
    initSite();
    try (ServerContext ctx = startServer()) {
      ProjectIndexCollection projectIndex =
          ctx.getInjector().getInstance(ProjectIndexCollection.class);
      Project.NameKey allProjects = ctx.getInjector().getInstance(AllProjectsName.class);
      Project.NameKey allUsers = ctx.getInjector().getInstance(AllUsersName.class);
      QueryOptions opts =
          QueryOptions.create(IndexConfig.createDefault(), 0, 1, ImmutableSet.of("name"));
      Optional<ProjectData> allProjectsData = projectIndex.getSearchIndex().get(allProjects, opts);
      assertThat(allProjectsData).isPresent();
      Optional<ProjectData> allUsersData = projectIndex.getSearchIndex().get(allUsers, opts);
      assertThat(allUsersData).isPresent();
    }
  }

  @Test
  public void initDoesNotReindexProjectsOnExistingSites() throws Exception {
    initSite();

    // Simulate a projects indexes files modified in the past by 3 seconds
    Optional<Instant> projectsLastModified =
        getProjectsIndexLastModified(sitePaths.index_dir).map(t -> t.minusSeconds(3));
    assertThat(projectsLastModified).isPresent();
    setProjectsIndexLastModifiedInThePast(sitePaths.index_dir, projectsLastModified.get());

    initSite();
    Optional<Instant> projectsLastModifiedAfterInit =
        getProjectsIndexLastModified(sitePaths.index_dir);

    // Verify that projects index files haven't been updated
    assertThat(projectsLastModified).isEqualTo(projectsLastModifiedAfterInit);
  }

  private void initSite() throws Exception {
    runGerrit("init", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
  }

  private void setProjectsIndexLastModifiedInThePast(Path indexDir, Instant time)
      throws IOException {
    for (Path path : getAllProjectsIndexFiles(indexDir).collect(Collectors.toList())) {
      FS.DETECTED.setLastModified(path, time);
    }
  }

  private Optional<Instant> getProjectsIndexLastModified(Path indexDir) throws IOException {
    return getAllProjectsIndexFiles(indexDir)
        .map(FS.DETECTED::lastModifiedInstant)
        .max(Comparator.comparingLong(Instant::toEpochMilli));
  }

  private Stream<Path> getAllProjectsIndexFiles(Path indexDir) throws IOException {
    Optional<Path> projectsPath =
        Files.walk(indexDir, 1)
            .filter(Files::isDirectory)
            .filter(p -> p.getFileName().toString().startsWith("projects_"))
            .findFirst();
    if (!projectsPath.isPresent()) {
      return Stream.empty();
    }

    return Files.walk(projectsPath.get(), 1, FileVisitOption.FOLLOW_LINKS);
  }
}

@@ -750,16 +750,71 @@
 bleSet;%0A
+import com.google.errorprone.annotations.MustBeClosed;%0A
 import c
@@ -3463,59 +3463,132 @@
     
-for (Path path : getAllProjectsIndexFiles(indexDir)
+try (Stream%3CPath%3E allprojectsIndexFiles = getAllProjectsIndexFiles(indexDir)) %7B%0A      for (Path path : allprojectsIndexFiles
 .col
@@ -3612,24 +3612,26 @@
 oList())) %7B%0A
+  
       FS.DET
@@ -3665,16 +3665,24 @@
  time);%0A
+      %7D%0A
     %7D%0A  
@@ -3781,22 +3781,57 @@
 n %7B%0A    
-return
+try (Stream%3CPath%3E allprojectsIndexFiles =
  getAllP
@@ -3857,17 +3857,57 @@
 ndexDir)
+) %7B
 %0A
+      return allprojectsIndexFiles%0A  
         
@@ -3953,16 +3953,18 @@
         
+  
 .max(Com
@@ -4008,26 +4008,48 @@
 hMilli));%0A  
-%7D%0A
+  %7D%0A  %7D%0A%0A  @MustBeClosed
 %0A  private S
@@ -4117,24 +4117,84 @@
 Exception %7B%0A
+    try (Stream%3CPath%3E stream = Files.walk(indexDir, 1)) %7B%0A  
     Optional
@@ -4227,32 +4227,19 @@
     
-Files.walk(indexDir, 1)%0A
+  stream%0A  
     
@@ -4278,32 +4278,34 @@
 ry)%0A            
+ 
+ 
 .filter(p -%3E p.g
@@ -4365,16 +4365,18 @@
         
+  
 .findFir
@@ -4377,24 +4377,26 @@
 indFirst();%0A
+  
     if (!pro
@@ -4422,24 +4422,26 @@
 ()) %7B%0A      
+  
 return Strea
@@ -4455,15 +4455,18 @@
 ();%0A
+  
     %7D%0A
-%0A
+  
     
@@ -4537,14 +4537,20 @@
 LINKS);%0A
+    %7D%0A
   %7D%0A%7D%0A

Streams that encapsulate a closeable resource should be closed