/*
 * 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.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.Template;
import org.jclouds.compute.domain.TemplateBuilder;
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 = "ArbitraryCpuRamTemplateBuilderImplTest")
public class ArbitraryCpuRamTemplateBuilderImplTest {
   private Location provider = new LocationBuilder()
         .scope(LocationScope.PROVIDER)
         .id("generic-provider")
         .description("generic-provider")
         .build();

   private Location region = new LocationBuilder()
         .scope(LocationScope.REGION)
         .id("us-east-1")
         .description("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("hardwareId")
         .name("hardwareName")
         .location(region)
         .uri(URI.create("uri"))
         .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 testAutoGeneratedHardwareFromId(){
      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 ArbitraryCpuRamTemplateBuilderImpl(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=256").build().getHardware();
      assertThat(hardware.getRam()).isEqualTo(256);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("automatic:cores=2.0;ram=256");

      Hardware hardware2 = templateBuilder.hardwareId("automatic:cores=2;ram=256;disk=100").build().getHardware();
      assertThat(hardware2.getRam()).isEqualTo(256);
      assertThat(hardware2.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware2.getId()).isEqualTo("automatic:cores=2.0;ram=256;disk=100");
      assertThat(hardware2.getVolumes().get(0).getSize()).isEqualTo(100);
   }

   @Test
   public void testAutoGeneratedHardwareWithMinCoresAndMinRam(){
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(1024);
      templateBuilder.minCores(4);
      Template template = templateBuilder.build();
      Hardware hardware = template.getHardware();
      assertThat(hardware.getRam()).isEqualTo(1024);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(4.0);
      assertThat(hardware.getId()).isEqualTo("automatic:cores=4.0;ram=1024");
   }

   @Test
   public void testAutoGeneratedHardwareWithMinCoresMinRamAndMinDisk() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(2);
      templateBuilder.minRam(2048);
      templateBuilder.minDisk(100);
      Template template = templateBuilder.build();
      Hardware hardware = template.getHardware();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("automatic:cores=2.0;ram=2048;disk=100");
      assertThat(hardware.getVolumes().get(0).getSize()).isEqualTo(100);
   }

   @Test(expectedExceptions = IllegalArgumentException.class,
         expectedExceptionsMessageRegExp = errorMessage)
   public void testAutoGeneratedHardwareWithMinCoresMinRamAndInvalidMinDisk() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
               Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
               Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(2);
      templateBuilder.minRam(4096);
      templateBuilder.minDisk(-100f);
      Template template = templateBuilder.build();
      Hardware hardware = template.getHardware();
      assertThat(hardware.getId()).isEqualTo("automatic:cores=2.0;ram=4096;disk=-100");
   }

   @Test
   public void testExistingHardwareProfileMatchHardwareProfileWithMinCoresMinRam() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(2);
      templateBuilder.minRam(1024);
      Template template = templateBuilder.build();
      Hardware hardware = template.getHardware();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("hardwareId");
   }

   @Test(expectedExceptions = IllegalArgumentException.class,
         expectedExceptionsMessageRegExp = errorMessage)
   public void testOnlyRam() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(4096);
      templateBuilder.build();
   }

   @Test(expectedExceptions = IllegalArgumentException.class,
         expectedExceptionsMessageRegExp = errorMessage)
   public void testOnlyCores() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(4);
      templateBuilder.build();
   }

   @Test
   public void testOnlyRamMatchHardwareProfile() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
                  Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minRam(1024);
      templateBuilder.build();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("hardwareId");
   }

   @Test
   public void testOnlyCoresMatchHardwareProfile() {
      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 ArbitraryCpuRamTemplateBuilderImpl(locations,
            new ImageCacheSupplier(images, 60,
                  Atomics.<AuthorizationException>newReference(), Providers.of(getImageStrategy)), hardwares,
            Suppliers.ofInstance(region), optionsProvider, templateBuilderProvider);
      templateBuilder.minCores(1);
      templateBuilder.build();
      assertThat(hardware.getRam()).isEqualTo(2048);
      assertThat(hardware.getProcessors()).extracting("cores").containsExactly(2.0);
      assertThat(hardware.getId()).isEqualTo("hardwareId");
   }

}
