// 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 com.google.common.base.MoreObjects;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.errors.NoSuchAccountException;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.LabelInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

class AddApprovalsStep {

  interface Factory {
    AddApprovalsStep create(Change change, ChangeInfo changeInfo, boolean resume);
  }

  private final AccountUtil accountUtil;
  private final ChangeUpdate.Factory updateFactory;
  private final ReviewDb db;
  private final IdentifiedUser.GenericFactory genericUserFactory;
  private final ChangeControl.GenericFactory changeControlFactory;
  private final Change change;
  private final ChangeInfo changeInfo;
  private final boolean resume;

  @Inject
  public AddApprovalsStep(AccountUtil accountUtil,
      ChangeUpdate.Factory updateFactory,
      ReviewDb db,
      IdentifiedUser.GenericFactory genericUserFactory,
      ChangeControl.GenericFactory changeControlFactory,
      @Assisted Change change,
      @Assisted ChangeInfo changeInfo,
      @Assisted boolean resume) {
    this.accountUtil = accountUtil;
    this.updateFactory = updateFactory;
    this.db = db;
    this.genericUserFactory = genericUserFactory;
    this.changeControlFactory = changeControlFactory;
    this.change = change;
    this.changeInfo = changeInfo;
    this.resume = resume;
  }

  void add(RemoteApi api) throws OrmException, NoSuchChangeException, IOException,
      NoSuchAccountException, BadRequestException {
    if (resume) {
      db.patchSetApprovals().delete(
          db.patchSetApprovals().byChange(change.getId()));
    }

    List<PatchSetApproval> approvals = new ArrayList<>();
    for (Entry<String, LabelInfo> e : changeInfo.labels.entrySet()) {
      String labelName = e.getKey();
      LabelInfo label = e.getValue();
      if (label.all != null) {
        for (ApprovalInfo a : label.all) {
          Account.Id user = accountUtil.resolveUser(api, a);
          ChangeControl ctrl = control(change, a);
          LabelType labelType = ctrl.getLabelTypes().byLabel(labelName);
          approvals.add(new PatchSetApproval(
              new PatchSetApproval.Key(change.currentPatchSetId(), user,
                  labelType.getLabelId()), a.value.shortValue(),
                  MoreObjects.firstNonNull(a.date, TimeUtil.nowTs())));
          ChangeUpdate update = updateFactory.create(ctrl);
          if (a.value != 0) {
            update.putApproval(labelName, a.value.shortValue());
          } else {
            update.removeApproval(labelName);
          }
          update.commit();
        }
      }
    }
    db.patchSetApprovals().insert(approvals);
  }

  private ChangeControl control(Change change, AccountInfo acc)
      throws NoSuchChangeException {
    return control(change, new Account.Id(acc._accountId));
  }

  private ChangeControl control(Change change, Account.Id id)
      throws NoSuchChangeException {
    return changeControlFactory.controlFor(change,
        genericUserFactory.create(id));
  }
}

@@ -841,32 +841,92 @@
 countException;%0A
+import com.google.gerrit.extensions.annotations.PluginName;%0A
 import com.googl
@@ -961,24 +961,24 @@
 ccountInfo;%0A
-
 import com.g
@@ -1754,16 +1754,74 @@
 isted;%0A%0A
+import org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0A%0A
 import j
@@ -2048,32 +2048,133 @@
 n resume);%0A  %7D%0A%0A
+  private static final Logger log = LoggerFactory%0A      .getLogger(ReplayInlineCommentsStep.class);%0A%0A
   private final 
@@ -2513,16 +2513,51 @@
  resume;
+%0A  private final String pluginName;
 %0A%0A  @Inj
@@ -2781,24 +2781,61 @@
 rolFactory,%0A
+      @PluginName String pluginName,%0A
       @Assis
@@ -3210,16 +3210,50 @@
 resume;%0A
+    this.pluginName = pluginName;%0A
   %7D%0A%0A  v
@@ -3891,24 +3891,24 @@
 change, a);%0A
-
           La
@@ -3960,32 +3960,527 @@
 bel(labelName);%0A
+          if(labelType == null) %7B%0A            log.warn(String.format(%22%5B%25s%5D Label '%25s' not found in target system.%22%0A                + %22 This label was referenced by an approval provided from '%25s'%22%0A                + %22 for change '%25s'.%22%0A                + %22 This approval will be skipped. In order to import this%22%0A                + %22 approval configure the missing label and resume the import.%22%0A                , pluginName, labelName, a.username, changeInfo.id));%0A            continue;%0A          %7D%0A
           approv

Handle labels not existing in the target system