/*****************************************************************************\
|                                               ( )_  _                       |
|    _ _    _ __   _ _    __    ___ ___     _ _ | ,_)(_)  ___   ___     _     |
|   ( '_`\ ( '__)/'_` ) /'_ `\/' _ ` _ `\ /'_` )| |  | |/',__)/' v `\ /'_`\   |
|   | (_) )| |  ( (_| |( (_) || ( ) ( ) |( (_| || |_ | |\__, \| (˅) |( (_) )  |
|   | ,__/'(_)  `\__,_)`\__  |(_) (_) (_)`\__,_)`\__)(_)(____/(_) (_)`\___/'  |
|   | |                ( )_) |                                                |
|   (_)                 \___/'                                                |
|                                                                             |
| General Bots Copyright (c) Pragmatismo.io. All rights reserved.             |
| Licensed under the AGPL-3.0.                                                |
|                                                                             |
| According to our dual licensing model, this program can be used either      |
| under the terms of the GNU Affero General Public License, version 3,        |
| or under a proprietary license.                                             |
|                                                                             |
| The texts of the GNU Affero General Public License with an additional       |
| permission and of our proprietary license can be found at and               |
| in the LICENSE file you have received along with this program.              |
|                                                                             |
| This program is distributed in the hope that it will be useful,             |
| but WITHOUT ANY WARRANTY, without even the implied warranty of              |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                |
| GNU Affero General Public License for more details.                         |
|                                                                             |
| "General Bots" is a registered trademark of Pragmatismo.io.                 |
| The licensing of the program under the AGPLv3 does not imply a              |
| trademark license. Therefore any rights, title and interest in              |
| our trademarks remain entirely with us.                                     |
|                                                                             |
\*****************************************************************************/
/**
 * @fileoverview General Bots server core.
 */
'use strict';
import urlJoin from 'url-join';
import { ServiceClient, WebResource } from '@azure/ms-rest-js';
import { CognitiveServicesManagementClient } from '@azure/arm-cognitiveservices';
import { ResourceManagementClient } from '@azure/arm-resources';
import { SubscriptionClient } from '@azure/arm-subscriptions';
import { SearchManagementClient } from '@azure/arm-search';
import { SqlManagementClient } from '@azure/arm-sql';
import { WebSiteManagementClient } from '@azure/arm-appservice';
import { GBLog } from 'botlib';
import { GBAdminService } from '../../../packages/admin.gbapp/services/GBAdminService.js';
import { GBCorePackage } from '../../../packages/core.gbapp/index.js';
import { GBConfigService } from '../../../packages/core.gbapp/services/GBConfigService.js';
import MicrosoftGraph from '@microsoft/microsoft-graph-client';
import { Spinner } from 'cli-spinner';
import * as publicIp from 'public-ip';
const WebSiteResponseTimeout = 900;
const iconUrl = 'https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png';
/**
 * Deployer for Microsoft cloud.
 */
export class AzureDeployerService {
    apiVersion = '2017-12-01';
    defaultEndPoint = 'http://localhost:4242';
    instance;
    cloud;
    webSiteClient;
    storageClient;
    cognitiveClient;
    searchClient;
    provider = 'Microsoft.BotService';
    subscriptionClient;
    accessToken;
    location;
    subscriptionId;
    farmName;
    deployer;
    core;
    freeTier;
    async runSearch(instance) {
        await this.deployer.rebuildIndex(instance, this.getKBSearchSchema(instance.searchIndex));
    }
    static async createInstance(deployer, freeTier = true) {
        const username = GBConfigService.get('CLOUD_USERNAME');
        const password = GBConfigService.get('CLOUD_PASSWORD');
        const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
        return await this.createInstanceWithCredentials(deployer, freeTier, subscriptionId, username, password);
    }
    static async createInstanceWithADALCredentials(deployer, freeTier = true, subscriptionId, credentials) {
        const service = new AzureDeployerService();
        service.core = deployer.core;
        service.deployer = deployer;
        service.freeTier = freeTier;
        const token = credentials['tokenCache']._entries[0];
        await service.initServices(token.accessToken, token.expiresOn, subscriptionId);
        return service;
    }
    static async createInstanceWithCredentials(deployer, freeTier = true, subscriptionId, username, password) {
        const service = new AzureDeployerService();
        const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
        return await this.createInstanceWithADALCredentials(deployer, freeTier, subscriptionId, credentials);
    }
    static createRequestObject(url, accessToken, verb, body) {
        const req = new WebResource();
        req.method = verb;
        req.url = url;
        req.headers.set('Content-Type', 'application/json');
        req.headers.set('accept-language', '*');
        req.headers.set('Authorization', `Bearer ${accessToken}`);
        req.body = body;
        return req;
    }
    async getSubscriptions(credentials) {
        const subscriptionClient = new SubscriptionClient(credentials);
        return subscriptionClient.subscriptions.list();
    }
    getKBSearchSchema(indexName) {
        return {
            name: indexName,
            fields: [
                {
                    name: 'questionId',
                    type: 'Edm.String',
                    searchable: false,
                    filterable: false,
                    retrievable: true,
                    sortable: false,
                    facetable: false,
                    key: true
                },
                {
                    name: 'subject1',
                    type: 'Edm.String',
                    searchable: true,
                    filterable: false,
                    retrievable: false,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'subject2',
                    type: 'Edm.String',
                    searchable: true,
                    filterable: false,
                    retrievable: false,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'subject3',
                    type: 'Edm.String',
                    searchable: true,
                    filterable: false,
                    retrievable: false,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'subject4',
                    type: 'Edm.String',
                    searchable: true,
                    filterable: false,
                    retrievable: false,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'content',
                    type: 'Edm.String',
                    searchable: true,
                    filterable: false,
                    retrievable: false,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'answerId',
                    type: 'Edm.Int32',
                    searchable: false,
                    filterable: false,
                    retrievable: true,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'instanceId',
                    type: 'Edm.Int32',
                    searchable: false,
                    filterable: true,
                    retrievable: true,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'skipIndex',
                    type: 'Edm.Boolean',
                    searchable: false,
                    filterable: true,
                    retrievable: true,
                    sortable: false,
                    facetable: false,
                    key: false
                },
                {
                    name: 'packageId',
                    type: 'Edm.Int32',
                    searchable: false,
                    filterable: true,
                    retrievable: true,
                    sortable: false,
                    facetable: false,
                    key: false
                }
            ],
            scoringProfiles: [],
            defaultScoringProfile: undefined,
            corsOptions: undefined
        };
    }
    async botExists(botId) {
        const baseUrl = `https://management.azure.com/`;
        const username = GBConfigService.get('CLOUD_USERNAME');
        const password = GBConfigService.get('CLOUD_PASSWORD');
        const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
        const httpClient = new ServiceClient();
        const query = `providers/${this.provider}/checkNameAvailability/Action?api-version=${this.apiVersion}`;
        const url = urlJoin(baseUrl, query);
        const body = {
            name: botId,
            type: 'botServices'
        };
        const req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(body));
        const res = await httpClient.sendRequest(req);
        return !res.parsedBody.valid;
    }
    async updateBotProxy(botId, group, endpoint) {
        const baseUrl = `https://management.azure.com/`;
        const username = GBConfigService.get('CLOUD_USERNAME');
        const password = GBConfigService.get('CLOUD_PASSWORD');
        const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
        const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
        const httpClient = new ServiceClient();
        const parameters = {
            properties: {
                endpoint: endpoint
            }
        };
        const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider}/botServices/${botId}?api-version=${this.apiVersion}`;
        const url = urlJoin(baseUrl, query);
        const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
        const res = await httpClient.sendRequest(req);
        // CHECK
        if (!JSON.parse(res.bodyAsText).id) {
            throw res.bodyAsText;
        }
        GBLog.info(`Bot proxy updated at: ${endpoint}.`);
    }
    async updateBot(botId, group, name, description, endpoint) {
        const baseUrl = `https://management.azure.com/`;
        const username = GBConfigService.get('CLOUD_USERNAME');
        const password = GBConfigService.get('CLOUD_PASSWORD');
        const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
        const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
        const httpClient = new ServiceClient();
        const parameters = {
            properties: {
                description: `${description}`,
                displayName: name,
                endpoint: endpoint,
                iconUrl: iconUrl
            }
        };
        const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider}/botServices/${botId}?api-version=${this.apiVersion}`;
        const url = urlJoin(baseUrl, query);
        const req = AzureDeployerService.createRequestObject(url, accessToken, 'PATCH', JSON.stringify(parameters));
        const res = await httpClient.sendRequest(req);
        // CHECK
        if (!JSON.parse(res.bodyAsText).id) {
            throw res.bodyAsText;
        }
        GBLog.info(`Bot updated at: ${endpoint}.`);
    }
    async deleteBot(botId, group) {
        const baseUrl = `https://management.azure.com/`;
        const username = GBConfigService.get('CLOUD_USERNAME');
        const password = GBConfigService.get('CLOUD_PASSWORD');
        const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
        const accessToken = await GBAdminService.getADALTokenFromUsername(username, password);
        const httpClient = new ServiceClient();
        const query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider}/botServices/${botId}?api-version=${this.apiVersion}`;
        const url = urlJoin(baseUrl, query);
        const req = AzureDeployerService.createRequestObject(url, accessToken, 'DELETE', undefined);
        const res = await httpClient.sendRequest(req);
        if (res.bodyAsText !== '') {
            throw res.bodyAsText;
        }
        GBLog.info(`Bot ${botId} was deleted from the provider.`);
    }
    async openStorageFirewall(groupName, serverName) {
        const subscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
        const ip = await publicIp.publicIpv4();
        let params = {
            startIpAddress: ip,
            endIpAddress: ip
        };
        await this.storageClient.firewallRules.createOrUpdate(groupName, serverName, 'gb', params);
    }
    async deployFarm(proxyAddress, instance, credentials, subscriptionId) {
        const culture = 'en-us';
        const token = credentials['tokenCache']._entries[0];
        await this.initServices(token.accessToken, token.expiresOn, subscriptionId);
        const spinner = new Spinner('%s');
        spinner.start();
        spinner.setSpinnerString('|/-\\');
        let keys;
        const name = instance.botId;
        GBLog.info(`Enabling resource providers...`);
        await this.enableResourceProviders('Microsoft.BotService');
        await this.enableResourceProviders('Microsoft.Web');
        await this.enableResourceProviders('Microsoft.Sql');
        GBLog.info(`Deploying Deploy Group (It may take a few minutes)...`);
        await this.createDeployGroup(name, instance.cloudLocation);
        GBLog.info(`Deploying Bot Server...`);
        const serverFarm = await this.createHostingPlan(name, `${name}-server-plan`, instance.cloudLocation);
        const serverName = `${name}-server`;
        await this.createServer(serverFarm.id, name, serverName, instance.cloudLocation);
        GBLog.info(`Deploying Bot Storage...`);
        const administratorLogin = `sa${GBAdminService.getRndReadableIdentifier()}`;
        const administratorPassword = GBAdminService.getRndPassword();
        const storageServer = `${name.toLowerCase()}-storage-server`;
        const storageName = `${name}-storage`;
        await this.createStorageServer(name, storageServer, administratorLogin, administratorPassword, storageServer, instance.cloudLocation);
        await this.createStorage(name, storageServer, storageName, instance.cloudLocation);
        instance.storageUsername = administratorLogin;
        instance.storagePassword = administratorPassword;
        instance.storageName = storageName;
        instance.storageDialect = 'mssql';
        instance.storageServer = `${storageServer}.database.windows.net`;
        GBLog.info(`Deploying Search...`);
        const searchName = `${name}-search`.toLowerCase();
        await this.createSearch(name, searchName, instance.cloudLocation);
        const searchKeys = await this.searchClient.adminKeys.get(name, searchName);
        instance.searchHost = `${searchName}.search.windows.net`;
        instance.searchIndex = 'azuresql-index';
        instance.searchIndexer = 'azuresql-indexer';
        instance.searchKey = searchKeys.primaryKey;
        // GBLog.info(`Deploying Speech...`);
        // const speech = await this.createSpeech(name, `${name}speech`, instance.cloudLocation);
        // keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
        // instance.speechEndpoint = speech.properties.endpoint;
        // instance.speechKey = keys.key1;
        // GBLog.info(`Deploying Text Analytics...`);
        // const textAnalytics = await this.createTextAnalytics(name, `${name}-textanalytics`, instance.cloudLocation);
        // instance.textAnalyticsEndpoint = textAnalytics.properties.endpoint.replace(`/text/analytics/v2.0`, '');
        GBLog.info(`Deploying SpellChecker...`);
        const spellChecker = await this.createSpellChecker(name, `${name}-spellchecker`);
        instance.spellcheckerEndpoint = spellChecker.properties.endpoint;
        // GBLog.info(`Deploying NLP...`);
        // const nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
        // const nlpa = await this.createNLPAuthoring(name, `${name}-nlpa`, instance.cloudLocation);
        // instance.nlpEndpoint = nlp.properties.endpoint;
        const sleep = ms => {
            return new Promise(resolve => {
                setTimeout(resolve, ms);
            });
        };
        GBLog.info(`Deploying Bot...`);
        instance.botEndpoint = this.defaultEndPoint;
        instance = await this.internalDeployBot(instance, this.accessToken, name, name, name, 'General BootBot', `${proxyAddress}/api/messages/${name}`, 'global', instance.nlpAppId, instance.nlpKey, instance.marketplaceId, instance.marketplacePassword, instance.cloudSubscriptionId);
        GBLog.info(`Waiting one minute to finish NLP service and keys creation...`);
        await sleep(60000);
        // keys = await this.cognitiveClient.accounts.listKeys(name, textAnalytics.name);
        // instance.textAnalyticsKey = keys.key1;
        keys = await this.cognitiveClient.accounts.listKeys(name, spellChecker.name);
        instance.spellcheckerKey = keys.key1;
        // let authoringKeys = await this.cognitiveClient.accounts.listKeys(name, nlpa.name);
        // keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
        // instance.nlpKey = keys.key1;
        // instance.nlpAuthoringKey = authoringKeys.key1;
        // const nlpAppId = await this.createNLPService(name, name, instance.cloudLocation, culture, instance.nlpAuthoringKey);
        // instance.nlpAppId = nlpAppId;
        GBLog.info('Updating server environment variables...');
        await this.updateWebisteConfig(name, serverName, serverFarm.id, instance);
        spinner.stop();
        return instance;
    }
    async deployToCloud(title, username, password, cloudLocation, authoringKey, appId, appPassword, subscriptionId) {
        const instance = {};
        instance.state = 'active';
        instance.botId = title;
        instance.cloudUsername = username;
        instance.cloudPassword = password;
        instance.cloudSubscriptionId = subscriptionId;
        instance.cloudLocation = cloudLocation;
        instance.nlpAuthoringKey = authoringKey;
        instance.marketplaceId = appId;
        instance.marketplacePassword = appPassword;
        instance.adminPass = GBAdminService.getRndPassword();
        const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
        // tslint:disable-next-line:no-http-string
        const url = `https://${instance.botId}.azurewebsites.net`;
        this.deployFarm(url, instance, credentials, subscriptionId);
    }
    /**
     * @see https://github.com/Azure/azure-rest-api-specs/blob/master/specification/botservice/resource-manager/Microsoft.BotService/preview/2017-12-01/botservice.json
     */
    async internalDeployBot(instance, accessToken, botId, name, group, description, endpoint, location, nlpAppId, nlpKey, appId, appPassword, subscriptionId) {
        return new Promise(async (resolve, reject) => {
            const baseUrl = `https://management.azure.com/`;
            await this.registerProviders(subscriptionId, baseUrl, accessToken);
            instance.marketplaceId = appId;
            instance.marketplacePassword = appPassword;
            instance.engineName = GBCorePackage['CurrentEngineName'];
            const parameters = {
                location: location,
                sku: {
                    name: this.freeTier ? 'F0' : 'S1'
                },
                name: botId,
                kind: 'bot',
                properties: {
                    description: description,
                    displayName: name,
                    endpoint: endpoint,
                    iconUrl: iconUrl,
                    luisAppIds: [nlpAppId],
                    luisKey: nlpKey,
                    msaAppId: appId,
                    msaAppPassword: appPassword,
                    enabledChannels: ['webchat', 'skype'],
                    configuredChannels: ['webchat', 'skype'] //, "facebook"]
                }
            };
            const httpClient = new ServiceClient();
            let query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/${this.provider}/botServices/${botId}?api-version=${this.apiVersion}`;
            let url = urlJoin(baseUrl, query);
            let req = AzureDeployerService.createRequestObject(url, accessToken, 'PUT', JSON.stringify(parameters));
            const res = await httpClient.sendRequest(req);
            if (!JSON.parse(res.bodyAsText).id) {
                reject(res.bodyAsText);
                return;
            }
            try {
                //tslint:disable-next-line:max-line-length
                query = `subscriptions/${subscriptionId}/resourceGroups/${group}/providers/Microsoft.BotService/botServices/${botId}/channels/WebChatChannel/listChannelWithKeys?api-version=${this.apiVersion}`;
                url = urlJoin(baseUrl, query);
                req = AzureDeployerService.createRequestObject(url, accessToken, 'POST', JSON.stringify(parameters));
                const resChannel = await httpClient.sendRequest(req);
                const key = JSON.parse(resChannel.bodyAsText).properties.properties.sites[0].key;
                instance.webchatKey = key;
                instance.whatsappBotKey = key;
                resolve(instance);
            }
            catch (error) {
                reject(error);
            }
        });
    }
    async syncBotServerRepository(group, name) {
        await this.webSiteClient.webApps.syncRepository(group, name);
    }
    async initServices(accessToken, expiresOnTimestamp, subscriptionId) {
        this.accessToken = accessToken;
        class AccessToken2 {
            expiresOnTimestamp;
            token;
        }
        class StaticAccessToken {
            getToken() {
                return new Promise(async (resolve, reject) => {
                    const t = new AccessToken2();
                    t.token = accessToken;
                    t.expiresOnTimestamp = expiresOnTimestamp;
                    resolve(t);
                });
            }
        }
        const token = new StaticAccessToken();
        this.cloud = new ResourceManagementClient(token, subscriptionId);
        this.webSiteClient = new WebSiteManagementClient(token, subscriptionId);
        this.storageClient = new SqlManagementClient(token, subscriptionId);
        this.cognitiveClient = new CognitiveServicesManagementClient(token, subscriptionId);
        this.searchClient = new SearchManagementClient(token, subscriptionId);
    }
    async createStorageServer(group, name, administratorLogin, administratorPassword, serverName, location) {
        const params = {
            location: location,
            administratorLogin: administratorLogin,
            administratorLoginPassword: administratorPassword,
            fullyQualifiedDomainName: serverName,
            requestOptions: { timeout: 60 * 1000 * 5 }
        };
        let database;
        try {
            database = await this.storageClient.servers.beginCreateOrUpdateAndWait(group, name, params);
        }
        catch (error) {
            // Try again (MSFT issues).
            GBLog.info('Storage (server) creation failed. Retrying...');
            database = await this.storageClient.servers.beginCreateOrUpdateAndWait(group, name, params);
        }
        // AllowAllWindowsAzureIps must be created that way, so the Azure Search can
        // access SQL Database to index its contents.
        const paramsFirewall = {
            startIpAddress: '0.0.0.0',
            endIpAddress: '0.0.0.0'
        };
        await this.storageClient.firewallRules.createOrUpdate(group, name, 'AllowAllWindowsAzureIps', paramsFirewall);
        return database;
    }
    async createApplication(token, name) {
        return new Promise((resolve, reject) => {
            let client = MicrosoftGraph.Client.init({
                authProvider: done => {
                    done(null, token);
                }
            });
            const app = {
                displayName: name
            };
            client.api(`/applications`).post(app, (err, res) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(res);
                }
            });
        });
    }
    async createApplicationSecret(token, appId) {
        return new Promise((resolve, reject) => {
            let client = MicrosoftGraph.Client.init({
                authProvider: done => {
                    done(null, token);
                }
            });
            const body = {
                passwordCredential: {
                    displayName: 'General Bots Generated'
                }
            };
            client.api(`/applications/${appId}/addPassword`).post(body, (err, res) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(res.secretText);
                }
            });
        });
    }
    async registerProviders(subscriptionId, baseUrl, accessToken) {
        const query = `subscriptions/${subscriptionId}/providers/${this.provider}/register?api-version=2018-02-01`;
        const requestUrl = urlJoin(baseUrl, query);
        const req = new WebResource();
        req.method = 'POST';
        req.url = requestUrl;
        req.headers = {};
        req.headers['Content-Type'] = 'application/json; charset=utf-8';
        req.headers['accept-language'] = '*';
        req.headers.Authorization = `Bearer ${accessToken}`;
    }
    async createNLPService(name, description, location, culture, authoringKey) {
        const parameters = {
            name: name,
            description: description,
            culture: culture
        };
        const body = JSON.stringify(parameters);
        const apps = await this.makeNlpRequest(location, authoringKey, undefined, 'GET', 'apps');
        let app = null;
        if (apps.bodyAsText && apps.bodyAsText !== '[]') {
            const result = JSON.parse(apps.bodyAsText);
            if (result.error) {
                if (result.error.code !== '401') {
                    throw new Error(result.error);
                }
            }
            else {
                app = result.filter(x => x.name === name)[0];
            }
        }
        let id;
        if (!app) {
            const res = await this.makeNlpRequest(location, authoringKey, body, 'POST', 'apps');
            id = res.bodyAsText;
        }
        else {
            id = app.id;
        }
        return id.replace(/\'/gi, '');
    }
    async makeNlpRequest(location, authoringKey, body, method, resource) {
        const req = new WebResource();
        req.method = method;
        req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
        req.headers.set('Content-Type', 'application/json');
        req.headers.set('accept-language', '*');
        req.headers.set('Ocp-Apim-Subscription-Key', authoringKey);
        req.body = body;
        const httpClient = new ServiceClient();
        return await httpClient.sendRequest(req);
    }
    async refreshEntityList(location, nlpAppId, clEntityId, nlpKey, data) {
        const req = new WebResource();
        req.method = 'PUT';
        req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/closedlists/${clEntityId}`;
        req.headers.set('Content-Type', 'application/json');
        req.headers.set('accept-language', '*');
        req.headers.set('Ocp-Apim-Subscription-Key', nlpKey);
        req.body = JSON.stringify(data);
        const httpClient = new ServiceClient();
        return await httpClient.sendRequest(req);
    }
    async trainNLP(location, nlpAppId, nlpAuthoringKey) {
        const req = new WebResource();
        req.method = 'POST';
        req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/versions/0.1/train`;
        req.headers.set('Content-Type', 'application/json');
        req.headers.set('accept-language', '*');
        req.headers.set('Ocp-Apim-Subscription-Key', nlpAuthoringKey);
        const httpClient = new ServiceClient();
        return await httpClient.sendRequest(req);
    }
    async publishNLP(location, nlpAppId, nlpAuthoringKey) {
        const body = {
            versionId: '0.1',
            isStaging: false,
            directVersionPublish: false
        };
        const req = new WebResource();
        req.method = 'POST';
        req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/${nlpAppId}/publish`;
        req.headers.set('Content-Type', 'application/json');
        req.headers.set('accept-language', '*');
        req.headers.set('Ocp-Apim-Subscription-Key', nlpAuthoringKey);
        req.body = JSON.stringify(body);
        const httpClient = new ServiceClient();
        return await httpClient.sendRequest(req);
    }
    async createSearch(group, name, location) {
        const params = {
            sku: {
                name: this.freeTier ? 'free' : 'standard'
            },
            location: location
        };
        return await this.searchClient.services.beginCreateOrUpdateAndWait(group, name, params);
    }
    async createStorage(group, serverName, name, location) {
        const params = {
            sku: { name: 'Basic' },
            createMode: 'Default',
            location: location
        };
        let database;
        try {
            database = await this.storageClient.databases.beginCreateOrUpdateAndWait(group, serverName, name, params);
        }
        catch (error) {
            // Try again (MSFT issues).
            GBLog.info('Storage (database) creation failed. Retrying...');
            database = await this.storageClient.databases.beginCreateOrUpdateAndWait(group, serverName, name, params);
        }
        return database;
    }
    async createCognitiveServices(group, name, location, kind) {
        const params = {
            sku: {
                name: name
            },
            createMode: 'Default',
            location: location,
            kind: kind,
            properties: {}
        };
        if (kind === 'LUIS.Authoring' || kind === 'LUIS') {
            params.sku.name = this.freeTier ? 'F0' : 'S0';
        }
        else if (kind === 'TextAnalytics') {
            params.sku.name = this.freeTier ? 'F0' : 'S0';
        }
        else if (kind === 'Bing.SpellCheck.v7') {
            params.sku.name = this.freeTier ? 'S0' : 'S1';
        }
        else if (kind === 'CognitiveServices') {
            params.sku.name = 'S0';
        }
        else if (kind === 'SpeechServices') {
            params.sku.name = this.freeTier ? 'F0' : 'S0';
        }
        return await this.cognitiveClient.accounts.beginCreateAndWait(group, name, params);
    }
    async createSpeech(group, name, location) {
        return await this.createCognitiveServices(group, name, location, 'SpeechServices');
    }
    async createNLP(group, name, location) {
        return await this.createCognitiveServices(group, name, location, 'LUIS');
    }
    async createNLPAuthoring(group, name, location) {
        return await this.createCognitiveServices(group, name, location, 'LUIS.Authoring');
    }
    async createSpellChecker(group, name) {
        return await this.createCognitiveServices(group, name, 'westus', 'CognitiveServices');
    }
    async createTextAnalytics(group, name, location) {
        return await this.createCognitiveServices(group, name, location, 'TextAnalytics');
    }
    async createDeployGroup(name, location) {
        const params = { location: location };
        return await this.cloud.resourceGroups.createOrUpdate(name, params);
    }
    async enableResourceProviders(name) {
        const ret = await this.cloud.providers.get(name);
        if (ret.registrationState === 'NotRegistered') {
            await this.cloud.providers.register(name);
        }
    }
    async createHostingPlan(group, name, location) {
        const params = {
            serverFarmWithRichSkuName: name,
            location: location,
            sku: {
                name: this.freeTier ? 'F1' : 'S1',
                capacity: 1,
                tier: this.freeTier ? 'Free' : 'Standard'
            }
        };
        return await this.webSiteClient.appServicePlans.beginCreateOrUpdateAndWait(group, name, params);
    }
    async createServer(farmId, group, name, location) {
        let tryed = false;
        const create = async () => {
            const parameters = {
                location: location,
                serverFarmId: farmId,
                siteConfig: {
                    nodeVersion: GBAdminService.getNodeVersion(),
                    detailedErrorLoggingEnabled: true,
                    requestTracingEnabled: true
                }
            };
            const server = await this.webSiteClient.webApps.beginCreateOrUpdateAndWait(group, name, parameters);
            const siteLogsConfig = {
                applicationLogs: {
                    fileSystem: { level: 'Error' }
                }
            };
            await this.webSiteClient.webApps.updateDiagnosticLogsConfig(group, name, siteLogsConfig);
            const souceControlConfig = {
                repoUrl: 'https://github.com/GeneralBots/BotServer.git',
                branch: 'master',
                isManualIntegration: true,
                isMercurial: false,
                deploymentRollbackEnabled: false
            };
            await this.webSiteClient.webApps.beginCreateOrUpdateSourceControlAndWait(group, name, souceControlConfig);
            return server;
        };
        try {
            return await create();
        }
        catch (e) {
            if (!tryed) {
                tryed = true;
                GBLog.info('Retrying Deploying Bot Server...');
                try {
                    return await create();
                }
                catch (error) {
                    GBLog.info('Server creation failed at all on MSAzure, stopping...');
                    throw error;
                }
            }
        }
    }
    async updateWebisteConfig(group, name, serverFarmId, instance) {
        const parameters = {
            location: instance.cloudLocation,
            serverFarmId: serverFarmId,
            siteConfig: {
                appSettings: [
                    { name: 'WEBSITES_CONTAINER_START_TIME_LIMIT', value: `${WebSiteResponseTimeout}` },
                    { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: GBAdminService.getNodeVersion() },
                    { name: 'ADDITIONAL_DEPLOY_PATH', value: `` },
                    { name: 'ADMIN_PASS', value: `${instance.adminPass}` },
                    { name: 'BOT_ID', value: `${instance.botId}` },
                    { name: 'CLOUD_SUBSCRIPTIONID', value: `${instance.cloudSubscriptionId}` },
                    { name: 'CLOUD_LOCATION', value: `${instance.cloudLocation}` },
                    { name: 'CLOUD_GROUP', value: `${instance.botId}` },
                    { name: 'CLOUD_USERNAME', value: `${instance.cloudUsername}` },
                    { name: 'CLOUD_PASSWORD', value: `${instance.cloudPassword}` },
                    { name: 'MARKETPLACE_ID', value: `${instance.marketplaceId}` },
                    { name: 'MARKETPLACE_SECRET', value: `${instance.marketplacePassword}` },
                    { name: 'STORAGE_DIALECT', value: `${instance.storageDialect}` },
                    { name: 'STORAGE_SERVER', value: `${instance.storageServer}.database.windows.net` },
                    { name: 'STORAGE_NAME', value: `${instance.storageName}` },
                    { name: 'STORAGE_USERNAME', value: `${instance.storageUsername}` },
                    { name: 'STORAGE_PASSWORD', value: `${instance.storagePassword}` },
                    { name: 'STORAGE_SYNC', value: `true` }
                ]
            }
        };
        return await this.webSiteClient.webApps.beginCreateOrUpdateAndWait(group, name, parameters);
    }
}
//# sourceMappingURL=AzureDeployerService.js.map