//Copyright (C) 2015 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.googlesource.gerrit.plugins.importer;

import static java.lang.String.format;

import com.google.gerrit.common.errors.NoSuchAccountException;
import com.google.gerrit.extensions.annotations.PluginData;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;

class ImportProjectTask implements Runnable {

  private static Logger log = LoggerFactory.getLogger(ImportProjectTask.class);

  interface Factory {
    ImportProjectTask create(
        @Assisted("from") String from,
        @Assisted Project.NameKey name,
        @Assisted("user") String user,
        @Assisted("password") String password,
        @Assisted StringBuffer result);
  }

  private final OpenRepositoryStep openRepoStep;
  private final ConfigureRepositoryStep configRepoStep;
  private final GitFetchStep gitFetchStep;
  private final ReplayChangesStep.Factory replayChangesFactory;
  private final File lockRoot;

  private final String fromGerrit;
  private final Project.NameKey name;
  private final String user;
  private final String password;
  private final StringBuffer messages;

  @Inject
  ImportProjectTask(OpenRepositoryStep openRepoStep,
      ConfigureRepositoryStep configRepoStep,
      GitFetchStep gitFetchStep,
      ReplayChangesStep.Factory replayChangesFactory,
      @PluginData File data,
      @Assisted("from") String fromGerrit,
      @Assisted Project.NameKey name,
      @Assisted("user") String user,
      @Assisted("password") String password,
      @Assisted StringBuffer messages) {
    this.openRepoStep = openRepoStep;
    this.configRepoStep = configRepoStep;
    this.gitFetchStep = gitFetchStep;
    this.replayChangesFactory = replayChangesFactory;
    this.lockRoot = data;

    this.fromGerrit = fromGerrit;
    this.name = name;
    this.user = user;
    this.password = password;
    this.messages = messages;
  }

  @Override
  public void run() {
    LockFile importing = lockForImport(name);
    if (importing == null) {
      return;
    }

    try {
      Repository repo = openRepoStep.open(name, messages);
      if (repo == null) {
        return;
      }

      try {
        configRepoStep.configure(repo, name, fromGerrit);
        gitFetchStep.fetch(user, password, repo, name, messages);
        replayChangesFactory.create(fromGerrit, user, password, repo, name)
            .replay();
      } catch (IOException | GitAPIException | OrmException
          | NoSuchAccountException | NoSuchChangeException | RestApiException
          | ValidationException e) {
        handleError(e);
      } finally {
        repo.close();
      }
    } catch (ResourceConflictException e) {
      handleError(e);
    } finally {
      importing.unlock();
      importing.commit();
    }
  }

  private void handleError(Exception e) {
    messages.append(format("Unable to transfer project '%s' from"
        + " source gerrit host '%s': %s. Check log for details.",
        name.get(), fromGerrit, e.getMessage()));
    log.error(format("Unable to transfer project '%s' from"
        + " source gerrit host '%s'.",
        name.get(), fromGerrit), e);
  }

  private LockFile lockForImport(Project.NameKey project) {
    File importStatus = new File(lockRoot, project.get());
    LockFile lockFile = new LockFile(importStatus, FS.DETECTED);
    try {
      if (lockFile.lock()) {
        return lockFile;
      } else {
        messages.append(format("Project %s is being imported from another session"
            + ", skipping", name.get()));
        return null;
      }
    } catch (IOException e1) {
      messages.append(format(
          "Error while trying to lock the project %s for import", name.get()));
      return null;
    }
  }
}

@@ -683,16 +683,56 @@
 ormat;%0A%0A
+import com.google.common.base.Charsets;%0A
 import c
@@ -778,32 +778,32 @@
 countException;%0A
-
 import com.googl
@@ -1025,24 +1025,70 @@
 nt.Project;%0A
+import com.google.gerrit.server.OutputFormat;%0A
 import com.g
@@ -1619,16 +1619,45 @@
 ception;
+%0Aimport java.io.OutputStream;
 %0A%0Aclass 
@@ -2459,16 +2459,46 @@
 sages;%0A%0A
+  private LockFile lockFile;%0A%0A
   @Injec
@@ -3298,25 +3298,25 @@
 ) %7B%0A    
-L
+l
 ockFile 
 importin
@@ -3307,26 +3307,16 @@
 ockFile 
-importing 
 = lockFo
@@ -3338,25 +3338,24 @@
     if (
-importing
+lockFile
  == null
@@ -3507,24 +3507,49 @@
       try %7B%0A
+        persistParams();%0A
         conf
@@ -4098,51 +4098,527 @@
     
-importing.unlock();%0A      importing.commit(
+try %7B%0A        lockFile.commit();%0A      %7D finally %7B%0A        lockFile.unlock();%0A      %7D%0A    %7D%0A  %7D%0A%0A  static class Params %7B%0A    String from;%0A    String user;%0A    String project;%0A  %7D%0A%0A  private void persistParams() throws IOException %7B%0A    Params p = new Params();%0A    p.from = fromGerrit;%0A    p.user = user;%0A    p.project = name.get();%0A%0A    String s = OutputFormat.JSON_COMPACT.newGson().toJson(p);%0A    try (OutputStream out = lockFile.getOutputStream()) %7B%0A      out.write(s.getBytes(Charsets.UTF_8));%0A      out.write('%5Cn'
 );%0A 

Persist the project import parameters for each imported project