/*
 * 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.serveraction.kerberos;

import static org.easymock.EasyMock.expect;
import static org.mockito.Matchers.anyBoolean;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import jakarta.persistence.EntityManager;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.Role;
import org.apache.ambari.server.RoleCommand;
import org.apache.ambari.server.actionmanager.HostRoleCommand;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.agent.ExecutionCommand;
import org.apache.ambari.server.agent.stomp.TopologyHolder;
import org.apache.ambari.server.audit.AuditLogger;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.RootComponent;
import org.apache.ambari.server.events.TopologyUpdateEvent;
import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.easymock.EasyMock;
import org.easymock.EasyMockSupport;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provider;

import junit.framework.Assert;

public class FinalizeKerberosServerActionTest extends EasyMockSupport {
  @Rule
  public TemporaryFolder folder = new TemporaryFolder();

  private final TopologyHolder topologyHolder = createNiceMock(TopologyHolder.class);

  @Test
  @Ignore("Update accordingly to changes")
  public void executeMITKDCOption() throws Exception {
    String clusterName = "c1";
    String clusterId = "1";
    Injector injector = setup(clusterName);

    File dataDirectory = createDataDirectory();

    Map<String, String> commandParams = new HashMap<>();
    commandParams.put(KerberosServerAction.KDC_TYPE, KDCType.MIT_KDC.name());
    commandParams.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());

    ExecutionCommand executionCommand = createMockExecutionCommand(clusterId, clusterName, commandParams);
    HostRoleCommand hostRoleCommand = createMockHostRoleCommand();

    PrincipalKeyCredential principleKeyCredential = createMock(PrincipalKeyCredential.class);

    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
    expect(kerberosHelper.getKDCAdministratorCredentials(clusterName)).andReturn(principleKeyCredential).anyTimes();

    replayAll();

    ConcurrentMap<String, Object> requestSharedDataContext = new ConcurrentHashMap<>();

    FinalizeKerberosServerAction action = new FinalizeKerberosServerAction(topologyHolder);
    action.setExecutionCommand(executionCommand);
    action.setHostRoleCommand(hostRoleCommand);

    Assert.assertTrue(dataDirectory.exists());

    CommandReport commandReport = action.execute(requestSharedDataContext);

    assertSuccess(commandReport);
    Assert.assertTrue(!dataDirectory.exists());

    verifyAll();
  }

  @Test
  public void executeManualOption() throws Exception {
    String clusterName = "c1";
    String clusterId = "1";
    Injector injector = setup(clusterName);

    File dataDirectory = createDataDirectory();

    Map<String, String> commandParams = new HashMap<>();
    commandParams.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());

    ExecutionCommand executionCommand = createMockExecutionCommand(clusterId, clusterName, commandParams);
    HostRoleCommand hostRoleCommand = createMockHostRoleCommand();

    replayAll();

    ConcurrentMap<String, Object> requestSharedDataContext = new ConcurrentHashMap<>();

    FinalizeKerberosServerAction action = new FinalizeKerberosServerAction(topologyHolder);
    action.setExecutionCommand(executionCommand);
    action.setHostRoleCommand(hostRoleCommand);

    Assert.assertTrue(dataDirectory.exists());

    CommandReport commandReport = action.execute(requestSharedDataContext);

    assertSuccess(commandReport);

    Assert.assertTrue(!dataDirectory.exists());

    verifyAll();
  }

  private File createDataDirectory() throws IOException {
    File directory = folder.newFolder();
    File dataDirectory = new File(directory, KerberosServerAction.DATA_DIRECTORY_PREFIX + "_test");

    Assert.assertTrue(dataDirectory.mkdir());

    return dataDirectory;
  }

  private void assertSuccess(CommandReport commandReport) {
    Assert.assertEquals(0, commandReport.getExitCode());
    Assert.assertEquals(HostRoleStatus.COMPLETED.name(), commandReport.getStatus());
    Assert.assertEquals("{}", commandReport.getStructuredOut());
  }

  private ExecutionCommand createMockExecutionCommand(String clusterId, String clusterName, Map<String, String> commandParams) {
    ExecutionCommand executionCommand = createMock(ExecutionCommand.class);
    expect(executionCommand.getClusterId()).andReturn(clusterId).anyTimes();
    expect(executionCommand.getClusterName()).andReturn(clusterName).anyTimes();
    expect(executionCommand.getCommandParams()).andReturn(commandParams).anyTimes();
    expect(executionCommand.getRoleCommand()).andReturn(RoleCommand.EXECUTE).anyTimes();
    expect(executionCommand.getRole()).andReturn(Role.AMBARI_SERVER_ACTION.name()).anyTimes();
    expect(executionCommand.getServiceName()).andReturn(RootComponent.AMBARI_SERVER.name()).anyTimes();
    expect(executionCommand.getTaskId()).andReturn(3L).anyTimes();

    return executionCommand;
  }

  private HostRoleCommand createMockHostRoleCommand() {
    HostRoleCommand hostRoleCommand = createMock(HostRoleCommand.class);

    expect(hostRoleCommand.getRequestId()).andReturn(1L).anyTimes();
    expect(hostRoleCommand.getStageId()).andReturn(2L).anyTimes();
    expect(hostRoleCommand.getTaskId()).andReturn(3L).anyTimes();

    return hostRoleCommand;
  }

  private Injector setup(String clusterName) throws AmbariException {
    final Map<String, Host> clusterHostMap = new HashMap<>();
    clusterHostMap.put("host1", createMock(Host.class));

    final ServiceComponentHost serviceComponentHost = createMock(ServiceComponentHost.class);
    expect(serviceComponentHost.getServiceName()).andReturn("SERVICE1").anyTimes();
    expect(serviceComponentHost.getServiceComponentName()).andReturn("COMPONENT1A").anyTimes();
    expect(serviceComponentHost.getHostName()).andReturn("host1").anyTimes();

    final List<ServiceComponentHost> serviceComponentHosts = new ArrayList<>();
    serviceComponentHosts.add(serviceComponentHost);

    final Cluster cluster = createMock(Cluster.class);
    expect(cluster.getClusterName()).andReturn(clusterName).anyTimes();
    expect(cluster.getServiceComponentHosts("host1")).andReturn(serviceComponentHosts).anyTimes();

    final Clusters clusters = createMock(Clusters.class);
    expect(clusters.getHostsForCluster(clusterName)).andReturn(clusterHostMap).anyTimes();
    expect(clusters.getCluster(clusterName)).andReturn(cluster).anyTimes();

    final TopologyUpdateEvent event = createNiceMock(TopologyUpdateEvent.class);
    expect(topologyHolder.getCurrentData()).andReturn(event).once();
    expect(topologyHolder.updateData(event)).andReturn(anyBoolean()).once();

    return Guice.createInjector(new AbstractModule() {

      @Override
      protected void configure() {
        bind(KerberosHelper.class).toInstance(createMock(KerberosHelper.class));
        bind(Clusters.class).toInstance(clusters);
        bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
        bind(EntityManager.class).toProvider((Provider<? extends EntityManager>) EasyMock.createNiceMock(Provider.class));
      }
    });
  }

}