const loginWithVmMSI = require("@azure/ms-rest-nodeauth").loginWithVmMSI;
const computeManagementClient = require("@azure/arm-compute").ComputeManagementClient;
const dnsManagementClient = require("@azure/arm-dns").DnsManagementClient;
const db = require("./mongo");
const utils = require("./utils");

const SUBSCRIPTION_ID = "62ebff8f-c40b-41be-9239-252d6c0c8ad9";

function _getRgName(provision) {
    let rgName = provision.scenario.toUpperCase();
    rgName = rgName.replace(/AZQMI/g, 'QMI');
    rgName = rgName + "-" + provision._id.toString();
    return rgName;
}

async function _getClient(scenarioName) {
    var id = SUBSCRIPTION_ID;
    if (scenarioName){
        var scenario = await db.scenario.getOne({"name": scenarioName});
        id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID;
    }
    var credentials = await loginWithVmMSI({ port: 50342 });
    //console.log("AzureCLI# authenticated", credentials);
    return new computeManagementClient(credentials, id);
}
async function _getDNSClient(scenarioName) {
    var id = SUBSCRIPTION_ID;
    if (scenarioName){
        var scenario = await db.scenario.getOne({"name": scenarioName});
        id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID;
    }
    var credentials = await loginWithVmMSI({ port: 50342 });
    //console.log("AzureCLI# authenticated", credentials);
    return new dnsManagementClient(credentials, id);
}



async function asyncForEach(array, callback) {
    for (let index = 0; index < array.length; index++) {
        await callback(array[index], index, array);
    }
}

async function deallocate(provision, isSendEmailAfter ) {
    
    let rgName = _getRgName(provision);
    console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);

    try {
        var computeClient = await _getClient(provision.scenario);
        let finalResult = await computeClient.virtualMachines.list(rgName);
        if ( finalResult && finalResult.length > 0 ) { 
            db.provision.update(provision._id, {"statusVms": "Stopping"});
        }

        await asyncForEach(finalResult, async function(vm)  {
            await computeClient.virtualMachines.deallocate(rgName, vm.name);
        });

        await utils.afterStopVms(provision, isSendEmailAfter);
        
        console.log("AzureCLI# All VMs DEALLOCATED for resource group: "+rgName);
    } catch ( error ) {
        console.log("AzureCLI# ERROR stopping VMs: "+rgName, error);
        await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
    }
}

async function start(provision){
    
    let rgName = _getRgName(provision);
    console.log("AzureCLI# Starting VMs for resource group: "+rgName);

    try { 

        var computeClient = await _getClient(provision.scenario);
        let finalResult = await computeClient.virtualMachines.list(rgName);

        if ( finalResult && finalResult.length > 0 ) {
            db.provision.update(provision._id, {"statusVms": "Starting"});
        }
        
        await asyncForEach(finalResult, async function(vm)  {
            await computeClient.virtualMachines.start(rgName, vm.name);
        });

        await utils.afterStartVms( provision );
        console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);

    } catch ( error ) {
        console.log("AzureCLI# ERROR starting VMs: "+rgName, error);
        await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
    }
    
    
}

async function getResourceGroupVms(rgName){
    let computeClient = await _getClient();
    return await computeClient.virtualMachines.list(rgName);
}

async function getAllVms(){ 
    let computeClient = await _getClient();
    console.log("AzureCLI# Retrieving all VMS from subscription");
    return await computeClient.virtualMachines.listAll();
}
async function getAllVmsNext(nextLink){ 
    let computeClient = await _getClient();
    console.log("AzureCLI# Retrieving all VMS from subscription (next): " +nextLink);
    return await computeClient.virtualMachines.listAllNext(nextLink)
}

async function updateVmsTags(provId, tagsEdit) {
    
    if ( !tagsEdit ){ return {} ; }
    let provision = await db.provision.getById(provId);
    if ( !provision ){ return };

    let rgName = _getRgName(provision);
    console.log("AzureCLI# Updating TAGS in VMs for resource group: "+rgName);

    
    var computeClient;
    var finalResult;
    try {
        computeClient = await _getClient(provision.scenario);
        finalResult = await computeClient.virtualMachines.list(rgName);
    } catch (e2) {
        console.log(`AzureCLI# ERROR: Resource group '${rgName}' could not be found`, e2);
        return {};
    }

    if (finalResult && finalResult.length > 0) {
        var toDelete = [];
        var toAdd = [];
        for(let tag in tagsEdit ) {
            if ( !tagsEdit[tag] ) {
                toDelete.push(tag);
            } else {
                toAdd.push({"key": tag, "value": tagsEdit[tag]});
            }
        }
        var result = {};
        if ( toDelete.length > 0 || toAdd.length > 0 ) {
            
            await asyncForEach(finalResult, async function(vm)  {
                
                var vmActualTags = JSON.stringify(vm.tags);
                var tags = vm.tags;
                toDelete.forEach(t=>{
                    delete tags[t];
                });
                toAdd.forEach(t=>{
                    tags[t.key] = t.value;
                });
                tags["ProvId"] = provision._id.toString();
                result[vm.name] = tags; 

                if ( JSON.stringify(tags) !== vmActualTags ) {
                    try {
                        console.log(`AzureCLI# setting new tags to VM ${vm.name} (${rgName})`);
                        await computeClient.virtualMachines.update(rgName, vm.name, {"tags": tags} );
                    } catch (e1) {
                        console.log(`AzureCLI# ERROR setting tags to VM ${vm.name} (${rgName})`, e1);
                    }
                } else {
                    console.log(`AzureCLI# Same tags, no need to update tags for VM ${vm.name} (${rgName})`);
                }
                
            });
        }

        //db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.vms-tags-update' });

        return result;

    } else {
        return {}
    }
};


/**
 * DNS Records
 */

const DNS_RESOURCE_GROUP = "QMI-Infra-DNS";
const DNS_ZONE_NAME = "qmi.qlik-poc.com";

async function getDNSRecords(type = "CNAME") { 
    let dnsClient = await _getDNSClient();
    console.log(`AzureCLI# Retrieving '${type}' DNS records`);
    return await dnsClient.recordSets.listByType(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, type);
}

async function createDNSRecord(provision, cname, type = "CNAME"){ 
    
    if ( !provision ) return;

    let rgName = _getRgName(provision).toLowerCase();

    let dnsClient = await _getDNSClient();
    console.log(`AzureCLI# Creating '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME} --> ${cname}`);
    let parameters = {
        "cnameRecord": {
            "cname": cname
        },
        "tTL": 60
    };
    return await dnsClient.recordSets.createOrUpdate(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type, parameters );
}

async function deleteDNSRecord(provision, type = "CNAME"){ 
    
    if ( !provision ) return;

    let rgName = _getRgName(provision).toLowerCase();
    let dnsClient = await _getDNSClient();
    console.log(`AzureCLI# Deleting '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME}`);
    return await dnsClient.recordSets.deleteMethod(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type );
}

module.exports.start = start;
module.exports.deallocate = deallocate;
module.exports.getResourceGroupVms = getResourceGroupVms;
module.exports.getAllVms = getAllVms;
module.exports.getAllVmsNext = getAllVmsNext;
module.exports.updateVmsTags = updateVmsTags;
module.exports.getDNSRecords = getDNSRecords;
module.exports.createDNSRecord = createDNSRecord;
module.exports.deleteDNSRecord = deleteDNSRecord
