package com.sequenceiq.it.cloudbreak.util.azure.azurevm.action;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.microsoft.azure.management.Azure;
import com.microsoft.azure.management.compute.PowerState;
import com.microsoft.azure.management.compute.VirtualMachine;
import com.microsoft.azure.management.resources.fluentcore.arm.models.HasId;
import com.sequenceiq.it.cloudbreak.log.Log;
import com.sequenceiq.it.cloudbreak.util.azure.AzureInstanceActionExecutor;
import com.sequenceiq.it.cloudbreak.util.azure.AzureInstanceActionResult;

import rx.schedulers.Schedulers;

@Component
public class AzureClientActions {
    private static final Logger LOGGER = LoggerFactory.getLogger(AzureClientActions.class);

    @Inject
    private Azure azure;

    public List<String> listInstanceVolumeIds(List<String> instanceIds) {
        List<String> diskIds = new ArrayList<>();
        instanceIds.forEach(id -> {
            String resourceGroup = getResourceGroupName(id);
            VirtualMachine vm = azure.virtualMachines().getByResourceGroup(resourceGroup, id);
            diskIds.addAll(vm.dataDisks().values().stream().map(HasId::id).collect(Collectors.toList()));
        });
        return diskIds;
    }

    public void deleteInstances(List<String> instanceIds) {
        AzureInstanceActionExecutor.builder()
                .onInstances(instanceIds)
                .withInstanceAction(id -> {
                    String resourceGroup = getResourceGroupName(id);
                    VirtualMachine vm = azure.virtualMachines().getByResourceGroup(resourceGroup, id);
                    LOGGER.info("Before deleting, vm {} power state is {}", id, vm.powerState());
                    return azure.virtualMachines().deleteByResourceGroupAsync(resourceGroup, id)
                            .doOnError(throwable -> Log.error(LOGGER, "Error when deleting instance {}: {}", id, throwable))
                            .subscribeOn(Schedulers.io());
                })
                .withInstanceStatusCheck(id -> {
                    String resourceGroup = getResourceGroupName(id);
                    VirtualMachine vm = azure.virtualMachines().getByResourceGroup(resourceGroup, id);
                    String powerState = getVmPowerState(vm);
                    String provisioningState = getVmProvisioningState(vm);
                    Log.log("Delete action is successful={} (expected true), vm {} power state is {}, provisioning state is {}",
                            vm == null, id, powerState, provisioningState);
                    return new AzureInstanceActionResult(vm == null, powerState, id);
                })
                .withTimeout(10, TimeUnit.MINUTES)
                .build()
                .execute();
        LOGGER.info("Deleting instances finished succesfully");
    }

    public void stopInstances(List<String> instanceIds) {
        AzureInstanceActionExecutor.builder()
                .onInstances(instanceIds)
                .withInstanceAction(id -> {
                    String resourceGroup = getResourceGroupName(id);
                    VirtualMachine vm = azure.virtualMachines().getByResourceGroup(resourceGroup, id);
                    LOGGER.info("Before stopping, vm {} power state is {}", id, vm.powerState());
                    return azure.virtualMachines().deallocateAsync(vm.resourceGroupName(), vm.name())
                            .doOnError(throwable -> Log.error(LOGGER, "Error when stopping instance {}: {}", id, throwable))
                            .subscribeOn(Schedulers.io());
                })
                .withInstanceStatusCheck(id -> {
                    String resourceGroup = getResourceGroupName(id);
                    VirtualMachine vm = azure.virtualMachines().getByResourceGroup(resourceGroup, id);
                    String powerState = getVmPowerState(vm);
                    String provisioningState = getVmProvisioningState(vm);
                    boolean success = PowerState.DEALLOCATED.toString().equals(powerState);
                    Log.log("Stop action isSuccessful={}, vm {} power state is {}, provisioning state is {}", success, id, powerState, provisioningState);
                    return new AzureInstanceActionResult(success, powerState, id);
                })
                .withTimeout(10, TimeUnit.MINUTES)
                .build()
                .execute();
        LOGGER.info("Stopping of instances finished succesfully");
    }

    private String getVmPowerState(VirtualMachine vm) {
        return Optional.ofNullable(vm).map(VirtualMachine::powerState).map(PowerState::toString).orElse("no power state");
    }

    private String getVmProvisioningState(VirtualMachine vm) {
        return Optional.ofNullable(vm).map(VirtualMachine::provisioningState).orElse("no provisioning state");
    }

    private String getResourceGroupName(String id) {
        return id.replaceFirst("..$", "");
    }

    public void setAzure(Azure azure) {
        this.azure = azure;
    }

    public Map<String, Map<String, String>> listTagsByInstanceId(List<String> instanceIds) {
        return instanceIds.stream()
                .map(id -> azure.virtualMachines().getByResourceGroup(getResourceGroupName(id), id))
                .collect(Collectors.toMap(VirtualMachine::id, VirtualMachine::tags));
    }
}

@@ -2530,83 +2530,8 @@
 m);%0A
-                    String provisioningState = getVmProvisioningState(vm);%0A
     
@@ -2589,24 +2589,8 @@
 l=%7B%7D
- (expected true)
 , vm
@@ -2614,64 +2614,26 @@
 s %7B%7D
-, provisioning state is %7B%7D%22,%0A                           
+ (expected null)%22,
  vm 
@@ -2655,35 +2655,16 @@
 werState
-, provisioningState
 );%0A     
@@ -3989,83 +3989,8 @@
 m);%0A
-                    String provisioningState = getVmProvisioningState(vm);%0A
     
@@ -4160,34 +4160,8 @@
  is 
-%7B%7D, provisioning state is 
 %7B%7D%22,
@@ -4188,27 +4188,8 @@
 tate
-, provisioningState
 );%0A 
@@ -4566,32 +4566,23 @@
 vm).map(
-VirtualMachine::
+v -%3E v.
 powerSta
@@ -4587,34 +4587,19 @@
 tate
+(
 ).
-map(PowerState::
 toString
 ).or
@@ -4594,16 +4594,18 @@
 toString
+()
 ).orElse
@@ -4610,203 +4610,20 @@
 se(%22
-no power state%22);%0A    %7D%0A%0A    private String getVmProvisioningState(VirtualMachine vm) %7B%0A        return Optional.ofNullable(vm).map(VirtualMachine::provisioningState).orElse(%22no provisioning state
+vm not found
 %22);%0A

Revert "CB-8360 Handle that Azure API does not return a power state every time"