/*
 * 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.jclouds.cloudstack.compute;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

import java.io.IOException;
import java.util.Map;

import org.jclouds.cloudstack.CloudStackContext;
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter;
import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.internal.BaseCloudStackComputeServiceContextExpectTest;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.util.Strings2;
import org.testng.annotations.Test;

import com.google.common.collect.ImmutableMap;
import com.google.inject.Injector;

/**
 * Tests the compute service abstraction of the cloudstack api.
 */
@Test(groups = "unit")
public class CloudStackComputeServiceAdapterExpectTest extends BaseCloudStackComputeServiceContextExpectTest<Injector> {
   HttpResponse deployVMResponse = HttpResponse.builder().statusCode(200)
         .payload(payloadFromResource("/deployvirtualmachineresponse.json"))
         .build();
  
   HttpRequest queryAsyncJobResult = HttpRequest.builder().method("GET")
        .endpoint("http://localhost:8080/client/api")
        .addQueryParam("response", "json")
        .addQueryParam("command", "queryAsyncJobResult")
        .addQueryParam("jobid", "50006")
        .addQueryParam("apiKey", "APIKEY")
        .addQueryParam("signature", "v8BWKMxd+IzHIuTaZ9sNSzCWqFI=")
        .addHeader("Accept", "application/json")
        .build();

   HttpResponse queryAsyncJobResultResponse = HttpResponse.builder().statusCode(200)
        .payload(payloadFromResource("/queryasyncjobresultresponse-virtualmachine.json"))
        .build();

   HttpResponse queryAsyncJobResultSecurityGroupResponse = HttpResponse.builder().statusCode(200)
        .payload(payloadFromResource("/queryasyncjobresultresponse-virtualmachine-securitygroup.json"))
        .build();

   HttpRequest queryAsyncJobResultAuthorizeIngress = HttpRequest.builder().method("GET")
        .endpoint("http://localhost:8080/client/api")
        .addQueryParam("response", "json")
        .addQueryParam("command", "queryAsyncJobResult")
        .addQueryParam("jobid", "13330fc9-8b3e-4582-aa3e-90883c041010")
        .addQueryParam("apiKey", "APIKEY")
        .addQueryParam("signature", "y4gk3ckWAMPDNZM26LUK0gAhfiE=")
        .addHeader("Accept", "application/json")
        .build();

   HttpResponse queryAsyncJobResultAuthorizeIngressResponse = HttpResponse.builder().statusCode(200)
        .payload(payloadFromResource("/queryasyncjobresultresponse-authorizeingress.json"))
        .build();
   
   HttpRequest listCapabilitiesNotListAll = HttpRequest.builder().method("GET")
     .endpoint("http://localhost:8080/client/api")
     .addQueryParam("response", "json")
     .addQueryParam("command", "listCapabilities")
     .addQueryParam("apiKey", "APIKEY")
     .addQueryParam("signature", "l3PVoJnKK2G2gHk3HPHtpwWjlW4=")
     .addHeader("Accept", "application/json")
     .build();

   public void testCreateNodeWithGroupEncodedIntoName() {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "1")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "4")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "M2Wx0CgOeH9vYHhbcbblwziqpwI=")
              .addHeader("Accept", "application/json")
              .build();
  
      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksResponse)
              .put(getZone, getZoneResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forNode = requestsSendResponses(requestResponseMap);

      Template template = forNode.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
      template.getOptions().as(CloudStackTemplateOptions.class).setupStaticNat(false);

      CloudStackComputeServiceAdapter adapter = forNode.getInstance(CloudStackComputeServiceAdapter.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertEquals(server.getCredentials(), LoginCredentials.builder().password("dD7jwajkh").build());
   }

   public void testCreateNodeWithGroupEncodedIntoNameWithKeyPair() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "1")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "4")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "mykeypair")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "D3qQlTNjxrBXeG82C7YPrwU1jMc=")
              .addHeader("Accept", "application/json")
              .build();
  
      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksResponse)
              .put(getZone, getZoneResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
      Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
      template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
         .setupStaticNat(false)
         .overrideLoginPrivateKey(privKey);

      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertEquals(server.getCredentials().getOptionalPrivateKey().get(), privKey);
   }

   public void testCreateNodeWithGroupEncodedIntoNameWithGenerateKeyPair() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "1")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "4")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "jclouds-test")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "kfU/J/REa4DdYj0b/pSjuB3h3Qc=")
              .addHeader("Accept", "application/json")
              .build();
  
      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksResponse)
              .put(getZone, getZoneResponse)
              .put(deployVM, deployVMResponse)
              .put(createSSHKeyPair, createSSHKeyPairResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
      template.getOptions().as(CloudStackTemplateOptions.class).generateKeyPair(true)
         .setupStaticNat(false);

      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertTrue(server.getCredentials().getOptionalPrivateKey().isPresent());
   }
   
   public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairDefaultSecurityGroup() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "2")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "241")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "mykeypair")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "5qkUSGh0y+P/t04/j3+EN9PAeFI=")
              .addHeader("Accept", "application/json")
              .build();
  
      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksWithSecurityGroupsResponse)
              .put(getZoneWithSecurityGroups, getZoneWithSecurityGroupsResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
      Template template = forKeyPair.getInstance(TemplateBuilder.class)
         .osFamily(OsFamily.CENTOS)
         .locationId("2")
         .build();
      template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
         .setupStaticNat(false)
         .overrideLoginPrivateKey(privKey);

      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertEquals(server.getCredentials().getOptionalPrivateKey().get(), privKey);
   }

   public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairDefaultSecurityGroupAndDisk() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "2")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "241")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "mykeypair")
              .addQueryParam("diskofferingid", "5678")
              .addQueryParam("size", "10")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "lDzBXtVKCktueskyI/haID9ohJU=")
              .addHeader("Accept", "application/json")
              .build();

      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksWithSecurityGroupsResponse)
              .put(getZoneWithSecurityGroups, getZoneWithSecurityGroupsResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
      Template template = forKeyPair.getInstance(TemplateBuilder.class)
              .osFamily(OsFamily.CENTOS)
              .locationId("2")
              .build();
      template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
              .diskOfferingId("5678")
              .dataDiskSize(10)
              .setupStaticNat(false)
              .overrideLoginPrivateKey(privKey);


      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
              template);
      assertNotNull(server);
      assertEquals(server.getCredentials().getOptionalPrivateKey().get(), privKey);
   }

   public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairGenerateSecurityGroup() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "2")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "241")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "mykeypair")
              .addQueryParam("securitygroupids", "30")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "rz8V/tMk/UbxUhNqp7Bq3CrSg/k=")
              .addHeader("Accept", "application/json")
              .build();
  
      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksWithSecurityGroupsResponse)
              .put(getZoneWithSecurityGroups, getZoneWithSecurityGroupsResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultSecurityGroupResponse)
              .put(queryAsyncJobResultAuthorizeIngress, queryAsyncJobResultAuthorizeIngressResponse)
              .put(getSecurityGroup, getSecurityGroupResponse)
              .put(createSecurityGroup, createSecurityGroupResponse)
              .put(authorizeIngress, authorizeIngressResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
      Template template = forKeyPair.getInstance(TemplateBuilder.class)
         .osFamily(OsFamily.CENTOS)
         .locationId("2")
         .build();
      template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
         .setupStaticNat(false)
         .generateSecurityGroup(true)
         .overrideLoginPrivateKey(privKey);

      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
      CloudStackContext context = forKeyPair.getInstance(CloudStackContext.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertEquals(server.getCredentials().getOptionalPrivateKey().get(), privKey);
   }

   public void testCreateNodeWithGroupEncodedIntoNameWithKeyPairAssignedToAccountAndDomain() throws IOException {
      HttpRequest deployVM = HttpRequest.builder().method("GET")
              .endpoint("http://localhost:8080/client/api")
              .addQueryParam("response", "json")
              .addQueryParam("command", "deployVirtualMachine")
              .addQueryParam("zoneid", "1")
              .addQueryParam("serviceofferingid", "1")
              .addQueryParam("templateid", "4")
              .addQueryParam("displayname", "test-e92")
              .addQueryParam("name", "test-e92")
              .addQueryParam("account", "account") //
              .addQueryParam("domainid", "domainId") //
              .addQueryParam("networkids", "204")
              .addQueryParam("group", "test")
              .addQueryParam("keypair", "mykeypair")
              .addQueryParam("apiKey", "APIKEY")
              .addQueryParam("signature", "hGV6gZZakwvNKhTJurkm48+zgso=")
              .addHeader("Accept", "application/json")
              .build();

      Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
              .put(listProjects, listProjectsResponse)
              .put(listTemplates, listTemplatesResponse)
              .put(listOsTypes, listOsTypesResponse)
              .put(listOsCategories, listOsCategoriesResponse)
              .put(listZones, listZonesResponse)
              .put(listServiceOfferings, listServiceOfferingsResponse)
              .put(listAccounts, listAccountsResponse)
              .put(listNetworks, listNetworksResponse)
              .put(getZone, getZoneResponse)
              .put(deployVM, deployVMResponse)
              .put(queryAsyncJobResult, queryAsyncJobResultResponse)
              .build();

      Injector forKeyPair = requestsSendResponses(requestResponseMap);

      String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
      Template template = forKeyPair.getInstance(TemplateBuilder.class).osFamily(OsFamily.CENTOS).build();
      template.getOptions().as(CloudStackTemplateOptions.class).keyPair("mykeypair")
         .account("account")
         .domainId("domainId")
         .setupStaticNat(false)
         .overrideLoginPrivateKey(privKey);

      CloudStackComputeServiceAdapter adapter = forKeyPair.getInstance(CloudStackComputeServiceAdapter.class);
      CloudStackContext context = forKeyPair.getInstance(CloudStackContext.class);

      NodeAndInitialCredentials<VirtualMachine> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
            template);
      assertNotNull(server);
      assertEquals(server.getCredentials().getOptionalPrivateKey().get(), privKey);
   }   
   
   @Override
   protected Injector clientFrom(CloudStackContext context) {
      return context.utils().injector();
   }
}
