text: // Copyright (c) Microsoft Corporation. All rights reserved.
  // Licensed under the MIT License.
  
  // bot.js is your main bot dialog entry point for handling activity types
  
  // Import required Bot Builder
  const { ActivityTypes, CardFactory, MessageFactory } = require('botbuilder');
  const { LuisRecognizer } = require('botbuilder-ai');
  const { DialogSet, DialogTurnStatus } = require('botbuilder-dialogs');
  
  const { UserProfile } = require('./dialogs/greeting/userProfile');
  const { EntityProfile } = require('./dialogs/entityProfile');
  const { WelcomeCard } = require('./dialogs/welcome');
  const { GreetingDialog } = require('./dialogs/greeting');
  const { CorrectionDialog } = require('./dialogs/correction');
  const { ReportDialog } = require('./dialogs/report');
  const { AssignmentDialog } = require('./dialogs/assignment');
  const { LoginDialog } = require('./dialogs/login');
  const { TransactionDialog } = require('./dialogs/transaction');
  
  // Greeting Dialog ID
  const GREETING_DIALOG = 'greetingDialog';
  
  // Correction Dialog ID
  const CORRECTION_DIALOG = 'correcctionDialog';
  
  // Report Dialog ID
  const REPORT_DIALOG = 'reportDialog';
  
  // Assignment Dialog ID
  const ASSIGNMENT_DIALOG = 'assignmentDialog';
  
  // Login Dialog ID
  const LOGIN_DIALOG = 'loginDialog';
  
  // Transaction Dialog ID
  const TRANSACTION_DIALOG = 'transactiontDialog';
  
  // State Accessor Properties
  const DIALOG_STATE_PROPERTY = 'dialogState';
  const USER_PROFILE_PROPERTY = 'userProfileProperty';
  const ENTITY_PROFILE_PROPERTY = 'entityProfileProperty';
  
  // LUIS service type entry as defined in the .bot file.
  const LUIS_CONFIGURATION = 'BasicBotLuisApplication';
  
  // Supported LUIS Intents.
  const GREETING_INTENT = 'Greeting';
  const CANCEL_INTENT = 'Cancel';
  const HELP_INTENT = 'Help';
  const NONE_INTENT = 'None';
  
  const ASSIGNMENT_INTENT = 'Assignment';
  const CORRECTION_INTENT = 'Correction';
  const LOGIN_INTENT = 'Login';
  const REPORT_INTENT = 'Report';
  const TRANSACTION_INTENT = 'Transaction';
  const FAQ_INTENT = 'FAQ';
  const YES_INTENT = 'Yes';
  const NO_INTENT = 'No';
  
  // Supported LUIS Entities, defined in ./dialogs/greeting/resources/greeting.lu
  const CORRECTION_ENTITIES = ['Correction'];
  const ASSIGNMENT_ENTITIES = ['Assignment'];
  const LOGIN_ENTITIES = ['Login'];
  const REPORT_ENTITIES = ['Report'];
  
  /**
   * Demonstrates the following concepts:
   *  Displaying a Welcome Card, using Adaptive Card technology
   *  Use LUIS to model Greetings, Help, and Cancel interactions
   *  Use a Waterfall dialog to model multi-turn conversation flow
   *  Use custom prompts to validate user input
   *  Store conversation and user state
   *  Handle conversation interruptions
   */
  class BasicBot {
      /**
       * Constructs the three pieces necessary for this bot to operate:
       * 1. StatePropertyAccessor for conversation state
       * 2. StatePropertyAccess for user state
       * 3. LUIS client
       * 4. DialogSet to handle our GreetingDialog
       *
       * @param {ConversationState} conversationState property accessor
       * @param {UserState} userState property accessor
       * @param {BotConfiguration} botConfig contents of the .bot file
       */
  
      constructor(conversationState, userState, botConfig) {
          if (!conversationState) throw new Error('Missing parameter.  conversationState is required');
          if (!userState) throw new Error('Missing parameter.  userState is required');
          if (!botConfig) throw new Error('Missing parameter.  botConfig is required');
  
          // Add the LUIS recognizer.
          const luisConfig = botConfig.findServiceByNameOrId(LUIS_CONFIGURATION);
          if (!luisConfig || !luisConfig.appId) throw new Error('Missing LUIS configuration. Please follow README.MD to create required LUIS applications.\n\n');
          const luisEndpoint = luisConfig.region && luisConfig.region.indexOf('https://') === 0 ? luisConfig.region : luisConfig.getEndpoint();
          this.luisRecognizer = new LuisRecognizer({
              applicationId: luisConfig.appId,
              endpoint: luisEndpoint,
              // CAUTION: Its better to assign and use a subscription key instead of authoring key here.
              endpointKey: luisConfig.authoringKey
          });
  
          // Create the property accessors for user and conversation state
          this.userProfileAccessor = userState.createProperty(USER_PROFILE_PROPERTY);
          this.dialogState = conversationState.createProperty(DIALOG_STATE_PROPERTY);
          this.entityProfileAccessor = userState.createProperty(ENTITY_PROFILE_PROPERTY);
  
          // Create top-level dialog(s)
          this.dialogs = new DialogSet(this.dialogState);
          // Add the Greeting dialog to the set
          this.dialogs.add(new GreetingDialog(GREETING_DIALOG, this.userProfileAccessor));
  
          // Add the Correction dialog to the set
          this.dialogs.add(new CorrectionDialog(CORRECTION_DIALOG, this.entityProfileAccessor));
  
          // Add the Report dialog to the set
          this.dialogs.add(new ReportDialog(REPORT_DIALOG, this.entityProfileAccessor));
  
          // Add the Assignment dialog to the set
          this.dialogs.add(new AssignmentDialog(ASSIGNMENT_DIALOG, this.entityProfileAccessor));
  
          // Add the Login dialog to the set
          this.dialogs.add(new LoginDialog(LOGIN_DIALOG, this.entityProfileAccessor));
  
          // Add the Transaction dialog to the set
          this.dialogs.add(new TransactionDialog(TRANSACTION_DIALOG, this.entityProfileAccessor));
  
          this.conversationState = conversationState;
          this.userState = userState;
      }
      /**
       * Driver code that does one of the following:
       * 1. Display a welcome card upon receiving ConversationUpdate activity
       * 2. Use LUIS to recognize intents for incoming user message
       * 3. Start a greeting dialog
       * 4. Optionally handle Cancel or Help interruptions
       *
       * @param {Context} context turn context from the adapter
       */
  
      async onTurn(context) {
          // Handle Message activity type, which is the main activity type for shown within a conversational interface
          // Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
          // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
          if (context.activity.type === ActivityTypes.Message) {
              let dialogResult;
              // Create a dialog context
              const dc = await this.dialogs.createContext(context);
  
              // Perform a call to LUIS to retrieve results for the current activity message.
              const results = await this.luisRecognizer.recognize(context);
  
              const topIntent = LuisRecognizer.topIntent(results);
  
              // update user profile property with any entities captured by LUIS
              // This could be user responding with their name or city while we are in the middle of greeting dialog,
              // or user saying something like 'i'm {userName}' while we have no active multi-turn dialog.
              //await this.updateUserProfile(results, context);
  
              await this.updateEntityProfile(results, context);
  
              // Based on LUIS topIntent, evaluate if we have an interruption.
              // Interruption here refers to user looking for help/ cancel existing dialog
              const interrupted = await this.isTurnInterrupted(dc, results);
              if (interrupted) {
                  if (dc.activeDialog !== undefined) {
                      // issue a re-prompt on the active dialog
                      dialogResult = await dc.repromptDialog();
                  } // Else: We dont have an active dialog so nothing to continue here.
              } else {
                  // No interruption. Continue any active dialogs.
                  dialogResult = await dc.continueDialog();
              }
  
  
              // If no active dialog or no active dialog has responded,
              if (!dc.context.responded) {
                  // Switch on return results from any active dialog.
                  switch (dialogResult.status) {
                      // dc.continueDialog() returns DialogTurnStatus.empty if there are no active dialogs
                      case DialogTurnStatus.empty:
                          // Determine what we should do based on the top intent from LUIS.
                          switch (topIntent) {
                              case GREETING_INTENT:
                                  await dc.beginDialog(GREETING_DIALOG);
                                  break;
  
                              case ASSIGNMENT_INTENT:
                                  //await dc.context.sendActivity(`Assignment intent detected, entities included: ${JSON.stringify(results.entities[topIntent])}`);
                                  await dc.beginDialog(ASSIGNMENT_DIALOG);
                                  break;
  
                              case LOGIN_INTENT:
                                  //await dc.context.sendActivity(`Login intent detected, entities included: ${JSON.stringify(results.entities[topIntent])}`);
                                  await dc.beginDialog(LOGIN_DIALOG);
                                  break;
  
                              case REPORT_INTENT:
                                  //await dc.context.sendActivity(`REPORT intent detected, entities included: ${JSON.stringify(results.entities[topIntent])}`);
                                  await dc.beginDialog(REPORT_DIALOG);
                                  break;
  
                              case CORRECTION_INTENT:
                                  //await dc.context.sendActivity(`Correction intent detected, entities included: ${JSON.stringify(results.entities[topIntent])}`);
                                  await dc.beginDialog(CORRECTION_DIALOG);
                                  break;
  
                              case TRANSACTION_INTENT:
                                  //await dc.context.sendActivity(`Transaction intent detected, entities included: ${JSON.stringify(results.entities[topIntent])}`);
                                  await dc.beginDialog(TRANSACTION_DIALOG);
                                  break;
                              case FAQ_INTENT:
                                  await this.sendFAQActions(context);
                                  break;
  
                              case NONE_INTENT:
                              default:
                                  // None or no intent identified, either way, let's provide some help
                                  // to the user
                                  await dc.context.sendActivity(`I didn't understand what you just said to me.`);
                                  break;
                          }
                          break;
                      case DialogTurnStatus.waiting:
                          // The active dialog is waiting for a response from the user, so do nothing.
                          break;
                      case DialogTurnStatus.complete:
                          // All child dialogs have ended. so do nothing.
                          break;
                      default:
                          // Unrecognized status from child dialog. Cancel all dialogs.
                          await dc.cancelAllDialogs();
                          break;
                  }
              }
          } else if (context.activity.type === ActivityTypes.ConversationUpdate) {
              // Handle ConversationUpdate activity type, which is used to indicates new members add to
              // the conversation.
              // see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types                
  
              // Send greeting when users are added to the conversation.
              //await this.sendWelcomeMessage(context);
              // Do we have any new members added to the conversation?
              if (context.activity.membersAdded.length !== 0) {
  
                  // Iterate over all new members added to the conversation
                  for (var idx in context.activity.membersAdded) {
                      // Greet anyone that was not the target (recipient) of this message
                      // the 'bot' is the recipient for events from the channel,
                      // context.activity.membersAdded == context.activity.recipient.Id indicates the
                      // bot was added to the conversation.
                      if (context.activity.membersAdded[idx].id == context.activity.recipient.id) {
                          // Welcome user.
                          // When activity type is "conversationUpdate" and the member joining the conversation is the bot
                          // we will send our Welcome Adaptive Card.  This will only be sent once, when the Bot joins conversation
                          // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
                          await this.sendFAQActions(context);
                          //const welcomeCard = CardFactory.adaptiveCard(WelcomeCard);
                          //await context.sendActivity({ attachments: [welcomeCard] });
                      }
                  }
              }
          }
  
          // make sure to persist state at the end of a turn.
          await this.conversationState.saveChanges(context);
          await this.userState.saveChanges(context);
      }
      /**
       * Look at the LUIS results and determine if we need to handle
       * an interruptions due to a Help or Cancel intent
       *
       * @param {DialogContext} dc - dialog context
       * @param {LuisResults} luisResults - LUIS recognizer results
       */
      async isTurnInterrupted(dc, luisResults) {
          const topIntent = LuisRecognizer.topIntent(luisResults);
  
          // see if there are anh conversation interrupts we need to handle
          if (topIntent === CANCEL_INTENT) {
              if (dc.activeDialog) {
                  // cancel all active dialog (clean the stack)
                  await dc.cancelAllDialogs();
                  await dc.context.sendActivity(`Ok.  I've cancelled our last activity.`);
              } else {
                  await dc.context.sendActivity(`I don't have anything to cancel.`);
              }
              return true; // this is an interruption
          }
  
          if (topIntent === HELP_INTENT) {
              await dc.context.sendActivity(`Let me try to provide some help.`);
              await dc.context.sendActivity(`I understand greetings, being asked for help, or being asked to cancel what I am doing.`);
              return true; // this is an interruption
          }
          return false; // this is not an interruption
      }
  
      /**
       * Helper function to update user profile with entities returned by LUIS.
       *
       * @param {LuisResults} luisResults - LUIS recognizer results
       * @param {DialogContext} dc - dialog context
       */
       
      async updateEntityProfile(luisResult, dc) {
          // get userProfile object using the accessor
          let entityProfile = await this.entityProfileAccessor.get(dc);
          // Do we have any entities?
          if (Object.keys(luisResult.entities).length !== 1) {
              if (entityProfile === undefined) {
                  entityProfile = new EntityProfile();
              }
              // see if we have any user name entities
              CORRECTION_ENTITIES.forEach(entity => {
                  if (luisResult.entities[entity] !== undefined) {
                      entityProfile.entity = luisResult.entities[entity][0];
                  }
              });
              REPORT_ENTITIES.forEach(entity => {
                  if (luisResult.entities[entity] !== undefined) {
                      entityProfile.entity = luisResult.entities[entity][0];
                  }
              });
              ASSIGNMENT_ENTITIES.forEach(entity => {
                  if (luisResult.entities[entity] !== undefined) {
                      entityProfile.entity = luisResult.entities[entity][0];
                  }
              });
              LOGIN_ENTITIES.forEach(entity => {
                  if (luisResult.entities[entity] !== undefined) {
                      entityProfile.entity = luisResult.entities[entity][0];
                  }
              });
              // set the new values
              await this.entityProfileAccessor.set(dc, entityProfile);
          }
          else {
              if (entityProfile && entityProfile.reset) {
                  // set the value to null
                  await this.entityProfileAccessor.set(dc, undefined);
              }
          }
      }
  
      /**
       * Helper function to update user profile with entities returned by LUIS.
       *
       * @param {LuisResults} luisResults - LUIS recognizer results
       * @param {DialogContext} dc - dialog context
       */
  
      async updateUserProfile(luisResult, context) {
          // Do we have any entities?
          if (Object.keys(luisResult.entities).length !== 1) {
              // get userProfile object using the accessor
              let userProfile = await this.userProfileAccessor.get(context);
              if (userProfile === undefined) {
                  userProfile = new UserProfile();
              }
              // see if we have any user name entities
              USER_NAME_ENTITIES.forEach(name => {
                  if (luisResult.entities[name] !== undefined) {
                      let lowerCaseName = luisResult.entities[name][0];
                      // capitalize and set user name
                      userProfile.name = lowerCaseName.charAt(0).toUpperCase() + lowerCaseName.substr(1);
                  }
              });
              USER_LOCATION_ENTITIES.forEach(city => {
                  if (luisResult.entities[city] !== undefined) {
                      let lowerCaseCity = luisResult.entities[city][0];
                      // capitalize and set user name
                      userProfile.city = lowerCaseCity.charAt(0).toUpperCase() + lowerCaseCity.substr(1);
                  }
              });
              // set the new values
              await this.userProfileAccessor.set(context, userProfile);
          }
      }
      
      /**
  
       * Send suggested actions to the user.
  
       * @param {TurnContext} turnContext A TurnContext instance containing all the data needed for processing this conversation turn.
  
       */
  
      async sendFAQActions(turnContext) {
  
          var reply = MessageFactory.suggestedActions(
              [
                  'Do you want to change your supervisor?',
                  'Do you want to change your backup approver?',
                  'Issue regarding Project assignment \ Hours submission?',
                  'Do you face application login issue?',
                  'Questions regarding Qlikview tool.',
                  'Questions regarding BIRT tool (Reports from RP Report tab).',
                  'Questions regarding Qliksense tool.',
                  'Do you receive the "Transaction not successfully started" error',
              ],
              'Here is the FAQ, ask away');
  
          await turnContext.sendActivity(reply);
      }
  }
  
  module.exports.BasicBot = BasicBot;