/*
 * 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.googlecomputeengine.compute.domain.internal;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Atomics;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.TemplateBuilderImpl;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.strategy.GetImageStrategy;
import org.jclouds.compute.suppliers.ImageCacheSupplier;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.rest.AuthorizationException;
import org.testng.annotations.Test;

import java.net.URI;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;

@Test(groups = "unit", singleThreaded = true, testName = "GoogleComputeEngineTemplateBuilderImplTest")
public class GoogleComputeEngineArbitraryCpuRamTemplateBuilderImplTest {
   private Location provider = new LocationBuilder()
         .scope(LocationScope.PROVIDER)
         .id("google-compute-engine")
         .description("google-compute-engine")
         .build();

   private Location region = new LocationBuilder()
         .scope(LocationScope.REGION)
         .id("us-east-1")
         .description("http://localhost/projects/party/zones/us-east-1")
         .parent(provider)
         .build();

   private OperatingSystem os = OperatingSystem.builder()
         .name("osName")
         .version("osVersion")
         .description("osDescription")
         .arch("X86_32")
         .build();

   private Image image = new ImageBuilder()
         .id("imageId")
         .providerId("imageId")
         .name("imageName")
         .description("imageDescription")
         .version("imageVersion")
         .operatingSystem(os)
         .status(Image.Status.AVAILABLE)
         .location(null)
         .build();

   private Hardware hardware = new HardwareBuilder()
         .ram(2048)
         .processor(new Processor(2, 1))
         .id("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2")
         .name("n2-standard-2")
         .location(region)
         .uri(URI.create("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2"))
         .build();

   private final String errorMessage = "No hardware profile matching the given criteria was found. If you want to use" +
           " exact values, please set the minCores, minRam and minDisk to positive values.";

   @Test
   public void testAutoGeneratedHardwareFromIdTest(){
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet.of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);

      TemplateBuilderImpl templateBuilder =
            new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations, new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region),
                  optionsProvider, templateBuilderProvider);

      Hardware hardware = templateBuilder.hardwareId("automatic:cores=2;ram=1024").build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(1024);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getUri()).isEqualTo(URI.create("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-1024"));
      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-1024");
   }

   @Test
   public void testAutoGeneratedHardwareWithMinCoresAndMinRamDontMatchTest() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(4096);
      templateBuilder.minCores(2);
      Hardware hardware = templateBuilder.build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(4096);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/custom-2-4096");
   }

   @Test
   public void testAutoGeneratedHardwareMatchHardwareProfile() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(1024);
      templateBuilder.minCores(2);
      Hardware hardware = templateBuilder.build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
   }

   @Test(expectedExceptions = IllegalArgumentException.class,
         expectedExceptionsMessageRegExp = errorMessage)
   public void testAutoGeneratedHardwareWithOnlyMinCoresTest() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(4);
      templateBuilder.build().getHardware();
   }

   @Test(expectedExceptions = IllegalArgumentException.class,
         expectedExceptionsMessageRegExp = errorMessage)
   public void testAutoGeneratedHardwareWithOnlyMinRamTest() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(4096);
      templateBuilder.build().getHardware();
   }

   @Test
   public void testAutoGeneratedHardwareWithOnlyMinCoresMatchedHardware() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(2);
      Hardware hardware = templateBuilder.build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId())
            .isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
   }

   @Test
   public void testAutoGeneratedHardwareOnlyRamMatchHardwareProfile() {
      Supplier<Set<? extends Location>> locations = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
            .of(region));
      Supplier<Set<? extends Image>> images = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet.of(image));
      Supplier<Set<? extends Hardware>> hardwares = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
            .<Hardware> of(hardware));
      Provider<TemplateOptions> optionsProvider = createMock(Provider.class);
      Provider<TemplateBuilder> templateBuilderProvider = createMock(Provider.class);
      TemplateBuilder defaultTemplate = createMock(TemplateBuilder.class);
      GetImageStrategy getImageStrategy = createMock(GetImageStrategy.class);

      expect(optionsProvider.get()).andReturn(new TemplateOptions());
      expect(getImageStrategy.getImage(anyObject(String.class))).andReturn(null);
      replay(defaultTemplate, optionsProvider, templateBuilderProvider, getImageStrategy);
      TemplateBuilderImpl templateBuilder = new GoogleComputeEngineArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(1024);
      Hardware hardware = templateBuilder.build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId())
            .isEqualTo("http://localhost/projects/party/zones/us-east-1/machineTypes/n2-standard-2");
   }

}
