const Alexa = require('ask-sdk');
const invocationName = 'recipe app';

function getMemoryAttributes() {
  const memoryAttributes = {
    'history': [],

    // The remaining attributes will be useful after DynamoDB persistence is configured
    'launchCount': 0,
    'lastUseTimestamp': 0,

    'lastSpeechOutput': {},
    'nextIntent': [],

  };
  return memoryAttributes;
}

const maxHistorySize = 20; // remember only latest 20 intents


/* HANDLERS */

const LaunchRequestHandler = {
    canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
      const responseBuilder = handlerInput.responseBuilder;
  
      const say = `${'hello and welcome to '}${invocationName} ! Say help to hear some options.`;
  
      const skillTitle = capitalize(invocationName);
  
  
      return responseBuilder
        .speak(say)
        .reprompt(`try again, ${say}`)
        .withStandardCard('Welcome!',
          `Hello!\nThis is a card for your skill, ${skillTitle}`,
          welcomeCardImg.smallImageUrl, welcomeCardImg.largeImageUrl)
        .getResponse();
    },
  };

// Handler for Ingredient Intent which uses slot type reference to populate slot values
const IngredientHandler = {
    canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'IngredientIntent';
    },
    handle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      const responseBuilder = handlerInput.responseBuilder;
  
      // delegate to Alexa to collect all the required slots
      let say = 'Hello. ';
  
      let slotStatus = '';
  
      const slotValues = getSlotValues(request.intent.slots);
  
      if (slotValues.Ingredient.heardAs) {
        slotStatus += ` You said ${slotValues.Ingredient.heardAs}, `;
      } else {
        slotStatus += 'Sorry I did not catch that ';
      }
      if (slotValues.Ingredient.ERstatus === 'ER_SUCCESS_MATCH') {
        slotStatus += 'a valid ';
        if (slotValues.Ingredient.resolved !== slotValues.Ingredient.heardAs) {
          slotStatus += `synonym for ${slotValues.Ingredient.resolved}. `;
        } else {
          slotStatus += 'match. ';
        } 
      }
      if (slotValues.Ingredient.ERstatus === 'ER_SUCCESS_NO_MATCH') {
        slotStatus += 'which did not match any slot value in the catalog list. ';
        console.log(`***** consider adding "${slotValues.Ingredient.heardAs}" to the custom slot type used by slot Ingredient! `);
      }
  
      if ((slotValues.Ingredient.ERstatus === 'ER_SUCCESS_NO_MATCH') 
            || (!slotValues.Ingredient.heardAs)) {
        slotStatus += 'A few valid values are salt, pepper, and vinegar';
      }
  
      say += slotStatus;
  
  
      return responseBuilder
        .speak(say)
        .reprompt(`try again, ${say}`)
        .getResponse();
    },
  };

  const HelpHandler = {
    canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'AMAZON.HelpIntent';
    },
    handle(handlerInput) {
      const responseBuilder = handlerInput.responseBuilder;
      const intents = getCustomIntents();
      const sampleIntent = randomElement(intents);
  
      let say = 'You asked for help. ';
      say += ` Here something you can ask me, ${getSampleUtterance(sampleIntent)}`;
  
      return responseBuilder
        .speak(say)
        .reprompt(`try again, ${say}`)
        .getResponse();
    },
  };

  const StopHandler = {
    canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'AMAZON.StopIntent';
    },
    handle(handlerInput) {
      const responseBuilder = handlerInput.responseBuilder;
      const say = 'Okay, talk to you later! ';
  
      return responseBuilder
        .speak(say)
        .withShouldEndSession(true)
        .getResponse();
    },
  };

const FallbackHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'AMAZON.FallbackIntent';
  },
  handle(handlerInput) {
    const responseBuilder = handlerInput.responseBuilder;
    const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
    const previousSpeech = getPreviousSpeechOutput(sessionAttributes);

    return responseBuilder
      .speak(`Sorry I didnt catch what you said, ${stripSpeak(previousSpeech.outputSpeech)}`)
      .reprompt(stripSpeak(previousSpeech.reprompt))
      .getResponse();
  },
};

const CancelHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'AMAZON.CancelIntent';
  },
  handle(handlerInput) {
    const responseBuilder = handlerInput.responseBuilder;
    const say = 'Okay, talk to you later! ';

    return responseBuilder
      .speak(say)
      .withShouldEndSession(true)
      .getResponse();
  },
};

const NavigateHomeHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest' 
        && request.intent.name 
            === 'AMAZON.NavigateHomeIntent';
  },
  handle(handlerInput) {
    const responseBuilder = handlerInput.responseBuilder;
    const say = 'Hello from AMAZON.NavigateHomeIntent. ';

    return responseBuilder
      .speak(say)
      .reprompt(`try again, ${say}`)
      .getResponse();
  },
};

const SessionEndedHandler = {
  canHandle(handlerInput) {
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    console.log(`Session ended with reason: ${handlerInput}`);
    console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
    return handlerInput.responseBuilder.getResponse();
  },
};

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('Sorry, an error occurred.  Please say again.')
      .reprompt('Sorry, an error occurred.  Please say again.')
      .getResponse();
  },
};


/* CONSTANTS */
const welcomeCardImg = {
    smallImageUrl: 'https://s3.amazonaws.com/skill-images-789/cards/card_plane720_480.png',
    largeImageUrl: 'https://s3.amazonaws.com/skill-images-789/cards/card_plane1200_800.png',
};

/* FUNCTIONS */
function capitalize(myString) {
  return myString.replace(/(?:^|\s)\S/g, (a) => a.toUpperCase());
}


function randomElement(myArray) {
  return (myArray[Math.floor(Math.random() * myArray.length)]);
}

function stripSpeak(str) {
  return (str.replace('<speak>', '').replace('</speak>', ''));
}


function getSlotValues(filledSlots) {
  const slotValues = {};

  Object.keys(filledSlots).forEach((item) => {
    const name = filledSlots[item].name;

    if (filledSlots[item]
            && filledSlots[item].resolutions
            && filledSlots[item].resolutions.resolutionsPerAuthority[0]
            && filledSlots[item].resolutions.resolutionsPerAuthority[0].status
            && filledSlots[item].resolutions.resolutionsPerAuthority[0].status.code) {
      switch (filledSlots[item].resolutions.resolutionsPerAuthority[0].status.code) {
        case 'ER_SUCCESS_MATCH':
          slotValues[name] = {
            heardAs: filledSlots[item].value,
            resolved: filledSlots[item].resolutions.resolutionsPerAuthority[0].values[0].value.name,
            ERstatus: 'ER_SUCCESS_MATCH',
          };
          break;
        case 'ER_SUCCESS_NO_MATCH':
          slotValues[name] = {
            heardAs: filledSlots[item].value,
            resolved: '',
            ERstatus: 'ER_SUCCESS_NO_MATCH',
          };
          break;
        default:
          break;
      }
    } else {
      slotValues[name] = {
        heardAs: filledSlots[item].value,
        resolved: '',
        ERstatus: '',
      };
    }
  }, this);

  return slotValues;
}

function getCustomIntents() {
  const modelIntents = model.interactionModel.languageModel.intents;

  const customIntents = [];


  for (let i = 0; i < modelIntents.length; i++) {
    if (modelIntents[i].name.substring(0, 7) !== 'AMAZON.' 
        && modelIntents[i].name 
            !== 'LaunchRequest') {
      customIntents.push(modelIntents[i]);
    }
  }
  return customIntents;
}

function getSampleUtterance(intent) {
  return randomElement(intent.samples);
}


function getPreviousSpeechOutput(attrs) {
  if (attrs.lastSpeechOutput && attrs.history.length > 1) {
    return attrs.lastSpeechOutput;
  }
  return false;
}

const InitMemoryAttributesInterceptor = {
  process(handlerInput) {
    let sessionAttributes = {};
    if (handlerInput.requestEnvelope.session.new) {
      sessionAttributes = handlerInput.attributesManager.getSessionAttributes();

      const memoryAttributes = getMemoryAttributes();

      if (Object.keys(sessionAttributes).length === 0) {
        Object.keys(memoryAttributes).forEach((key) => {
          sessionAttributes[key] = memoryAttributes[key];
        });
      }
      handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
    }
  },
};

const RequestHistoryInterceptor = {
  process(handlerInput) {
    const thisRequest = handlerInput.requestEnvelope.request;
    const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();

    const history = sessionAttributes.history || [];

    let IntentRequest = {};
    if (thisRequest.type === 'IntentRequest') {
      const slots = [];

      IntentRequest = {
        'IntentRequest': thisRequest.intent.name,
      };

      if (thisRequest.intent.slots) {
        for (const slot in thisRequest.intent.slots) {
          const slotObj = {};
          slotObj[slot] = thisRequest.intent.slots[slot].value;
          slots.push(slotObj);
        }

        IntentRequest = {
          'IntentRequest': thisRequest.intent.name,
          'slots': slots,
        };
      }
    } else {
      IntentRequest = { 'IntentRequest': thisRequest.type };
    }
    if (history.length > maxHistorySize - 1) {
      history.shift();
    }
    history.push(IntentRequest);

    handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
  },

};


const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
  .addRequestHandlers(
    FallbackHandler,
    CancelHandler,
    HelpHandler,
    StopHandler,
    NavigateHomeHandler,
    IngredientHandler,
    LaunchRequestHandler,
    SessionEndedHandler,
  )
  .addErrorHandlers(ErrorHandler)
  .addRequestInterceptors(InitMemoryAttributesInterceptor)
  .addRequestInterceptors(RequestHistoryInterceptor)
  .lambda();


// End of Skill code -------------------------------------------------------------
// Static Language Model for reference

const model = {
  'interactionModel': {
    'languageModel': {
      'invocationName': 'recipe app',
      'intents': [
        {
          'name': 'AMAZON.FallbackIntent',
          'samples': [],
        },
        {
          'name': 'AMAZON.CancelIntent',
          'samples': [],
        },
        {
          'name': 'AMAZON.HelpIntent',
          'samples': [],
        },
        {
          'name': 'AMAZON.StopIntent',
          'samples': [],
        },
        {
          'name': 'AMAZON.NavigateHomeIntent',
          'samples': [],
        },
        {
          'name': 'IngredientIntent',
          'slots': [
            {
              'name': 'Ingredient',
              'slotTypeReference': {
                'id': 'amzn1.ask.interactionModel.slotType.a5d9602f-8657-474e-bf0e-1b03c43ad459',
                'version': '1'
              }
            },
          ],
          'samples': [
            'Add {Ingredient} to my ingredient list',
          ],
        },
        {
          'name': 'LaunchRequest',
        },
      ],
      'types': [
        {
          'slotTypeReference': {
            'id': 'amzn1.ask.interactionModel.slotType.a5d9602f-8657-474e-bf0e-1b03c43ad459',
            'version': '1'
          },
          'values': [ 
            {
              'id': 'id13',
              'name': {
                'value': 'olive oil'
              }
            },
            {
              'id': 'id13',
              'name': {
                'value': 'lemon'
              }
            } 
          ]
        }
      ],
    },
  },
};
