// Copyright (C) 2013 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;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.gerrit.server.notedb.NoteDbTable.CHANGES;

import com.google.common.collect.ImmutableSet;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Set;
import org.eclipse.jgit.lib.Config;

/**
 * Implement NoteDb migration stages using {@code gerrit.config}.
 *
 * <p>This class controls the state of the migration according to options in {@code gerrit.config}.
 * In general, any changes to these options should only be made by adventurous administrators, who
 * know what they're doing, on non-production data, for the purposes of testing the NoteDb
 * implementation. Changing options quite likely requires re-running {@code RebuildNoteDb}. For
 * these reasons, the options remain undocumented.
 */
@Singleton
public class ConfigNotesMigration extends NotesMigration {
  public static class Module extends AbstractModule {
    @Override
    public void configure() {
      bind(NotesMigration.class).to(ConfigNotesMigration.class);
    }
  }

  public static final String SECTION_NOTE_DB = "noteDb";

  // All of these names must be reflected in the allowed set in checkConfig.
  private static final String DISABLE_REVIEW_DB = "disableReviewDb";
  private static final String FUSE_UPDATES = "fuseUpdates";
  private static final String PRIMARY_STORAGE = "primaryStorage";
  private static final String READ = "read";
  private static final String SEQUENCE = "sequence";
  private static final String WRITE = "write";

  private static void checkConfig(Config cfg) {
    Set<String> keys = ImmutableSet.of(CHANGES.key());
    Set<String> allowed =
        ImmutableSet.of(
            DISABLE_REVIEW_DB.toLowerCase(),
            PRIMARY_STORAGE.toLowerCase(),
            READ.toLowerCase(),
            WRITE.toLowerCase(),
            SEQUENCE.toLowerCase());
    for (String t : cfg.getSubsections(SECTION_NOTE_DB)) {
      checkArgument(keys.contains(t.toLowerCase()), "invalid NoteDb table: %s", t);
      for (String key : cfg.getNames(SECTION_NOTE_DB, t)) {
        checkArgument(allowed.contains(key.toLowerCase()), "invalid NoteDb key: %s.%s", t, key);
      }
    }
  }

  public static Config allEnabledConfig() {
    Config cfg = new Config();
    cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, true);
    cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, true);
    cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, true);
    cfg.setString(SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.NOTE_DB.name());
    cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, true);
    // TODO(dborowitz): Set to true when FileRepository supports it.
    cfg.setBoolean(SECTION_NOTE_DB, CHANGES.key(), FUSE_UPDATES, false);
    return cfg;
  }

  private final boolean writeChanges;
  private final boolean readChanges;
  private final boolean readChangeSequence;
  private final PrimaryStorage changePrimaryStorage;
  private final boolean disableChangeReviewDb;
  private final boolean fuseUpdates;

  @Inject
  public ConfigNotesMigration(@GerritServerConfig Config cfg) {
    checkConfig(cfg);

    writeChanges = cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), WRITE, false);
    readChanges = cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), READ, false);

    // Reading change sequence numbers from NoteDb is not the default even if
    // reading changes themselves is. Once this is enabled, it's not easy to
    // undo: ReviewDb might hand out numbers that have already been assigned by
    // NoteDb. This decision for the default may be reevaluated later.
    readChangeSequence = cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), SEQUENCE, false);

    changePrimaryStorage =
        cfg.getEnum(SECTION_NOTE_DB, CHANGES.key(), PRIMARY_STORAGE, PrimaryStorage.REVIEW_DB);
    disableChangeReviewDb =
        cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), DISABLE_REVIEW_DB, false);
    fuseUpdates = cfg.getBoolean(SECTION_NOTE_DB, CHANGES.key(), FUSE_UPDATES, false);

    checkArgument(
        !(disableChangeReviewDb && changePrimaryStorage != PrimaryStorage.NOTE_DB),
        "cannot disable ReviewDb for changes if default change primary storage is ReviewDb");
  }

  @Override
  public boolean rawWriteChangesSetting() {
    return writeChanges;
  }

  @Override
  public boolean readChanges() {
    return readChanges;
  }

  @Override
  public boolean readChangeSequence() {
    return readChangeSequence;
  }

  @Override
  public PrimaryStorage changePrimaryStorage() {
    return changePrimaryStorage;
  }

  @Override
  public boolean disableChangeReviewDb() {
    return disableChangeReviewDb;
  }

  @Override
  public boolean fuseUpdates() {
    return fuseUpdates;
  }
}

@@ -782,55 +782,8 @@
 S;%0A%0A
-import com.google.common.collect.ImmutableSet;%0A
 impo
@@ -837,16 +837,16 @@
 Config;%0A
+
 import c
@@ -1024,30 +1024,8 @@
 on;%0A
-import java.util.Set;%0A
 impo
@@ -1877,85 +1877,8 @@
 %22;%0A%0A
-  // All of these names must be reflected in the allowed set in checkConfig.%0A
   pr
@@ -2218,671 +2218,8 @@
 %22;%0A%0A
-  private static void checkConfig(Config cfg) %7B%0A    Set%3CString%3E keys = ImmutableSet.of(CHANGES.key());%0A    Set%3CString%3E allowed =%0A        ImmutableSet.of(%0A            DISABLE_REVIEW_DB.toLowerCase(),%0A            PRIMARY_STORAGE.toLowerCase(),%0A            READ.toLowerCase(),%0A            WRITE.toLowerCase(),%0A            SEQUENCE.toLowerCase());%0A    for (String t : cfg.getSubsections(SECTION_NOTE_DB)) %7B%0A      checkArgument(keys.contains(t.toLowerCase()), %22invalid NoteDb table: %25s%22, t);%0A      for (String key : cfg.getNames(SECTION_NOTE_DB, t)) %7B%0A        checkArgument(allowed.contains(key.toLowerCase()), %22invalid NoteDb key: %25s.%25s%22, t, key);%0A      %7D%0A    %7D%0A  %7D%0A%0A
   pu
@@ -3092,16 +3092,16 @@
 @Inject%0A
+
   public
@@ -3160,31 +3160,8 @@
 ) %7B%0A
-    checkConfig(cfg);%0A%0A
     

Remove config sanity check