// Copyright (C) 2016 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.server.notedb.rebuild;

import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gwtorm.server.OrmException;
import java.sql.Timestamp;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class ChangeMessageEvent extends Event {
  private static final ImmutableMap<Change.Status, Pattern> STATUS_PATTERNS =
      ImmutableMap.of(
          Change.Status.ABANDONED, Pattern.compile("^Abandoned(\n.*)*$"),
          Change.Status.MERGED,
              Pattern.compile(
                  "^Change has been successfully (merged|cherry-picked|rebased|pushed).*$"),
          Change.Status.NEW, Pattern.compile("^Restored(\n.*)*$"));

  private static final Pattern PRIVATE_SET_REGEXP = Pattern.compile("^Set private$");
  private static final Pattern PRIVATE_UNSET_REGEXP = Pattern.compile("^Unset private$");

  private static final Pattern TOPIC_SET_REGEXP = Pattern.compile("^Topic set to (.+)$");
  private static final Pattern TOPIC_CHANGED_REGEXP =
      Pattern.compile("^Topic changed from (.+) to (.+)$");
  private static final Pattern TOPIC_REMOVED_REGEXP = Pattern.compile("^Topic (.+) removed$");

  private static final Pattern WIP_SET_REGEXP = Pattern.compile("^Set Work In Progress$");
  private static final Pattern WIP_UNSET_REGEXP = Pattern.compile("^Set Ready For Review$");

  private final Change change;
  private final Change noteDbChange;
  private final Optional<Change.Status> status;
  private final ChangeMessage message;

  ChangeMessageEvent(
      Change change, Change noteDbChange, ChangeMessage message, Timestamp changeCreatedOn) {
    super(
        message.getPatchSetId(),
        message.getAuthor(),
        message.getRealAuthor(),
        message.getWrittenOn(),
        changeCreatedOn,
        message.getTag());
    this.change = change;
    this.noteDbChange = noteDbChange;
    this.message = message;
    this.status = parseStatus(message);
  }

  @Override
  boolean uniquePerUpdate() {
    return true;
  }

  @Override
  protected boolean isSubmit() {
    return status.isPresent() && status.get() == Change.Status.MERGED;
  }

  @Override
  protected boolean canHaveTag() {
    return true;
  }

  @SuppressWarnings("deprecation")
  @Override
  void apply(ChangeUpdate update) throws OrmException {
    checkUpdate(update);
    update.setChangeMessage(message.getMessage());
    setPrivate(update);
    setTopic(update);
    setWorkInProgress(update);

    if (status.isPresent()) {
      Change.Status s = status.get();
      update.fixStatus(s);
      noteDbChange.setStatus(s);
      if (s == Change.Status.MERGED) {
        update.setSubmissionId(change.getSubmissionId());
        noteDbChange.setSubmissionId(change.getSubmissionId());
      }
    }
  }

  private static Optional<Change.Status> parseStatus(ChangeMessage message) {
    String msg = message.getMessage();
    if (msg == null) {
      return Optional.empty();
    }
    for (Map.Entry<Change.Status, Pattern> e : STATUS_PATTERNS.entrySet()) {
      if (e.getValue().matcher(msg).matches()) {
        return Optional.of(e.getKey());
      }
    }
    return Optional.empty();
  }

  private void setPrivate(ChangeUpdate update) {
    String msg = message.getMessage();
    if (msg == null) {
      return;
    }
    Matcher m = PRIVATE_SET_REGEXP.matcher(msg);
    if (m.matches()) {
      update.setPrivate(true);
      noteDbChange.setPrivate(true);
      return;
    }

    m = PRIVATE_UNSET_REGEXP.matcher(msg);
    if (m.matches()) {
      update.setPrivate(false);
      noteDbChange.setPrivate(false);
    }
  }

  private void setTopic(ChangeUpdate update) {
    String msg = message.getMessage();
    if (msg == null) {
      return;
    }
    Matcher m = TOPIC_SET_REGEXP.matcher(msg);
    if (m.matches()) {
      String topic = m.group(1);
      update.setTopic(topic);
      noteDbChange.setTopic(topic);
      return;
    }

    m = TOPIC_CHANGED_REGEXP.matcher(msg);
    if (m.matches()) {
      String topic = m.group(2);
      update.setTopic(topic);
      noteDbChange.setTopic(topic);
      return;
    }

    if (TOPIC_REMOVED_REGEXP.matcher(msg).matches()) {
      update.setTopic(null);
      noteDbChange.setTopic(null);
    }
  }

  private void setWorkInProgress(ChangeUpdate update) {
    String msg = message.getMessage();
    if (msg == null) {
      return;
    }
    Matcher m = WIP_SET_REGEXP.matcher(msg);
    if (m.matches()) {
      update.setWorkInProgress(true);
      noteDbChange.setWorkInProgress(true);
      return;
    }

    m = WIP_UNSET_REGEXP.matcher(msg);
    if (m.matches()) {
      update.setWorkInProgress(false);
      noteDbChange.setWorkInProgress(false);
    }
  }

  @Override
  protected void addToString(ToStringHelper helper) {
    helper.add("message", message);
  }
}

@@ -706,24 +706,63 @@
 ringHelper;%0A
+import com.google.common.base.Strings;%0A
 import com.g
@@ -897,24 +897,76 @@
 ngeMessage;%0A
+import com.google.gerrit.server.ChangeMessagesUtil;%0A
 import com.g
@@ -5244,32 +5244,52 @@
    String msg = 
+Strings.nullToEmpty(
 message.getMessa
@@ -5296,68 +5296,176 @@
 ge()
+)
 ;%0A    
-if (msg == null) %7B%0A      return;
+String tag = message.getTag();%0A    if (ChangeMessagesUtil.TAG_SET_WIP.equals(tag)
 %0A    
-%7D%0A
     
-Matcher m =
+%7C%7C ChangeMessagesUtil.TAG_UPLOADED_WIP_PATCH_SET.equals(tag)%0A        %7C%7C
  WIP
@@ -5484,35 +5484,24 @@
 matcher(msg)
-;%0A    if (m
 .matches()) 
@@ -5592,32 +5592,139 @@
     
-  return;%0A    %7D%0A%0A    m =
+%7D else if (ChangeMessagesUtil.TAG_SET_READY.equals(tag)%0A        %7C%7C ChangeMessagesUtil.TAG_UPLOADED_PATCH_SET.equals(tag)%0A        %7C%7C
  WIP
@@ -5745,35 +5745,24 @@
 matcher(msg)
-;%0A    if (m
 .matches()) 

Check message tag when converting WIP state to NoteDb