/*
 * 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.logsearch.configurer;

import org.apache.ambari.logsearch.conf.SolrAuditLogPropsConfig;
import org.apache.ambari.logsearch.conf.global.SolrAuditLogsState;
import org.apache.ambari.logsearch.dao.AuditSolrDao;
import org.apache.ambari.logsearch.handler.ListCollectionHandler;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class SolrAuditAliasConfigurer implements Configurer {

  private static final Logger logger = LogManager.getLogger(SolrAuditAliasConfigurer.class);

  private static final int ALIAS_SETUP_RETRY_SECOND = 30 * 60;

  private final AuditSolrDao auditSolrDao;

  public SolrAuditAliasConfigurer(final AuditSolrDao auditSolrDao) {
    this.auditSolrDao = auditSolrDao;
  }

  @Override
  public void start() {
    final SolrAuditLogPropsConfig solrPropsConfig = (SolrAuditLogPropsConfig) auditSolrDao.getSolrPropsConfig();
    final SolrAuditLogsState state = (SolrAuditLogsState) auditSolrDao.getSolrCollectionState();
    final Collection<String> collectionListIn =
      Arrays.asList(solrPropsConfig.getCollection(), solrPropsConfig.getRangerCollection().trim());

    if (solrPropsConfig.getAliasNameIn() == null || collectionListIn.size() == 0) {
      logger.info("Will not create alias {} for {}", solrPropsConfig.getAliasNameIn(), collectionListIn.toString());
      return;
    }

    logger.info("setupAlias " + solrPropsConfig.getAliasNameIn() + " for " + collectionListIn.toString());
    // Start a background thread to do setup
    Thread setupThread = new Thread("setup_alias_" + solrPropsConfig.getAliasNameIn()) {
      @Override
      public void run() {
        logger.info("Started monitoring thread to check availability of Solr server. alias=" + solrPropsConfig.getAliasNameIn() +
          ", collections=" + collectionListIn.toString());
        int retryCount = 0;
        while (true) {
          if (state.isSolrCollectionReady()) {
            try {
              CloudSolrClient solrClient = auditSolrDao.getSolrClient();
              int count = createAlias(solrClient, solrPropsConfig.getAliasNameIn(), collectionListIn);
              if (count > 0) {
                solrClient.setDefaultCollection(solrPropsConfig.getAliasNameIn());
                if (count == collectionListIn.size()) {
                  logger.info("Setup for alias " + solrPropsConfig.getAliasNameIn() + " is successful. Exiting setup retry thread. " +
                    "Collections=" + collectionListIn);
                  state.setSolrAliasReady(true);
                  break;
                }
              } else {
                logger.warn("Not able to create alias=" + solrPropsConfig.getAliasNameIn() + ", retryCount=" + retryCount);
              }
            } catch (Exception e) {
              logger.error("Error setting up alias=" + solrPropsConfig.getAliasNameIn(), e);
            }
          }
          try {
            Thread.sleep(ALIAS_SETUP_RETRY_SECOND * 1000);
          } catch (InterruptedException sleepInterrupted) {
            logger.info("Sleep interrupted while setting up alias " + solrPropsConfig.getAliasNameIn());
            break;
          }
          retryCount++;
        }
      }
    };
    setupThread.setDaemon(true);
    setupThread.start();
  }

  private int createAlias(final CloudSolrClient solrClient, String aliasNameIn, Collection<String> collectionListIn)
    throws SolrServerException, IOException {
    List<String> collectionToAdd = new ArrayList<>();
    try {
      collectionToAdd = new ListCollectionHandler().handle(solrClient, null);
    } catch (Exception e) {
      logger.error("Invalid state during getting collections for creating alias");
    }
    collectionToAdd.retainAll(collectionListIn);

    String collectionsCSV = null;
    if (!collectionToAdd.isEmpty()) {
      collectionsCSV = StringUtils.join(collectionToAdd, ',');
      CollectionAdminRequest.CreateAlias aliasCreateRequest = CollectionAdminRequest.createAlias(aliasNameIn, collectionsCSV);
      CollectionAdminResponse createResponse = aliasCreateRequest.process(solrClient);
      if (createResponse.getStatus() != 0) {
        logger.error("Error creating alias. alias=" + aliasNameIn + ", collectionList=" + collectionsCSV
          + ", response=" + createResponse);
        return 0;
      }
    }
    if (collectionToAdd.size() == collectionListIn.size()) {
      logger.info("Created alias for all collections. alias=" + aliasNameIn + ", collectionsCSV=" + collectionsCSV);
    } else {
      logger.info("Created alias for " + collectionToAdd.size() + " out of " + collectionListIn.size() + " collections. " +
        "alias=" + aliasNameIn + ", collectionsCSV=" + collectionsCSV);
    }
    return collectionToAdd.size();
  }
}