/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.ambari.server.checks;

import java.util.Iterator;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.audit.AuditLoggerModule;
import org.apache.ambari.server.controller.ControllerModule;
import org.apache.ambari.server.ldap.LdapModule;
import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.utils.EventBusSynchronizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.persist.PersistService;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;

public class DatabaseConsistencyChecker {
  private static final Logger LOG = LoggerFactory.getLogger
          (DatabaseConsistencyChecker.class);


  private PersistService persistService;
  private DBAccessor dbAccessor;
  private Injector injector;


  @Inject
  public DatabaseConsistencyChecker(DBAccessor dbAccessor,
                             Injector injector,
                             PersistService persistService) {
    this.dbAccessor = dbAccessor;
    this.injector = injector;
    this.persistService = persistService;
  }

  /**
   * Extension of main controller module
   */
  public static class CheckHelperControllerModule extends ControllerModule {

    public CheckHelperControllerModule() throws Exception {
    }

    @Override
    protected void configure() {
      super.configure();
      EventBusSynchronizer.synchronizeAmbariEventPublisher(binder());
    }
  }

  /**
   * Extension of audit logger module
   */
  public static class CheckHelperAuditModule extends AuditLoggerModule {

    public CheckHelperAuditModule() throws Exception {
    }

    @Override
    protected void configure() {
      super.configure();
    }

  }

  public void startPersistenceService() {
    persistService.start();
  }

  public void stopPersistenceService() {
    persistService.stop();
  }

  /*
  * Main method from which we are calling all checks
  * */
  public static void main(String[] args) throws Exception {
    DatabaseConsistencyChecker databaseConsistencyChecker = null;
    try {

      Injector injector = Guice.createInjector(new CheckHelperControllerModule(), new CheckHelperAuditModule(), new LdapModule());
      databaseConsistencyChecker = injector.getInstance(DatabaseConsistencyChecker.class);

      databaseConsistencyChecker.startPersistenceService();

      DatabaseConsistencyCheckHelper.runAllDBChecks(false);
      DatabaseConsistencyCheckHelper.checkHostComponentStates();

      DatabaseConsistencyCheckHelper.checkServiceConfigs();

      databaseConsistencyChecker.stopPersistenceService();

    } catch (Throwable e) {
      if (e instanceof AmbariException) {
        LOG.error("Exception occurred during database check:", e);
        throw (AmbariException)e;
      } else {
        LOG.error("Unexpected error, database check failed", e);
        throw new Exception("Unexpected error, database check failed", e);
      }
    } finally {
        DatabaseConsistencyCheckHelper.closeConnection();
        if (DatabaseConsistencyCheckHelper.getLastCheckResult().isErrorOrWarning()) {
          String ambariDBConsistencyCheckLog = "ambari-server-check-database.log";
          ch.qos.logback.classic.Logger dbConsistencyCheckHelperLogger =
                  (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(DatabaseConsistencyCheckHelper.class);

          for (Iterator<Appender<ILoggingEvent>> index = dbConsistencyCheckHelperLogger.iteratorForAppenders(); index.hasNext();){
            Appender<ILoggingEvent> appender = index.next();
            if (appender instanceof FileAppender) {
              ambariDBConsistencyCheckLog = ((FileAppender) appender).getFile();
              break;
            }
          }
          ambariDBConsistencyCheckLog = ambariDBConsistencyCheckLog.replace("//", "/");

          if (DatabaseConsistencyCheckHelper.getLastCheckResult().isError()) {
            System.out.print(String.format("DB configs consistency check failed. Run \"ambari-server start --skip-database-check\" to skip. " +
                  "You may try --auto-fix-database flag to attempt to fix issues automatically. " +
                  "If you use this \"--skip-database-check\" option, do not make any changes to your cluster topology " +
                  "or perform a cluster upgrade until you correct the database consistency issues. See \"%s\" " +
                  "for more details on the consistency issues.", ambariDBConsistencyCheckLog));
          } else {
            System.out.print(String.format("DB configs consistency check found warnings. See \"%s\" " +
                    "for more details.", ambariDBConsistencyCheckLog));
          }
        } else {
          System.out.print("No errors and warnings were found.");
        }

    }
  }


}
