// This code is licensed under the same terms as Habitica:
  // https://raw.githubusercontent.com/HabitRPG/habitrpg/develop/LICENSE

/* ========================================== */
/* [Users] Required script data to fill in    */
/* ========================================== */
const USER_ID = "PasteYourUserIdHere"
const API_TOKEN = "PasteYourApiTokenHere" // Do not share this to anyone
const WEB_APP_URL = "PasteGeneratedWebAppUrlHere"

/* ========================================== */
/* [Users] Required customizations to fill in */
/* ========================================== */

// Which version do you want to use?
// If you want to heal a set number when leveling up (such as 4.5 HP), select 2.
// If you want to heal a percent of your remaining health (such as 25%), select 3.
const VERSION = 2

// How much HP do you want to gain each level-up? Fill in the correct one (set value or percent of remaining health)
const HP_SET_VALUE_PER_LEVEL = 4.5 // Enter a number between 0 and 50.
const HP_PERCENT_PER_LEVEL = 50.0 // Do not put a percent sign after the number here. Enter a number between 0 and 100.

/* ========================================== */
/* [Users] Optional customizations to fill in */
/* ========================================== */

// Do you want a button that lets you enter/exit Partial Healing Mode? If yes, change the 0 to a 1 below.
// If you don't have a button, you can still enter/exit the Mode by manually running the function "togglePartialHealingMode"
const PARTIAL_HEALING_TOGGLE_BUTTON = 0

/* ========================================== */
/* [Users] Do not edit code below this line   */
/* ========================================== */
const AUTHOR_ID = "0034eb14-b4d8-494e-8386-d3f33cff7922"
const SCRIPT_NAME = "Partial Healing Mode"
const HEADERS = {
  "x-client" : AUTHOR_ID + " - " + SCRIPT_NAME,
  "x-api-user" : USER_ID,
  "x-api-key" : API_TOKEN,
}

const scriptProperties = PropertiesService.getScriptProperties(); // Constants can have properties changed

// Button title and notes change based on whether you are in Partial Healing Mode or not
const PARTIAL_HEALING_MODE_TEXT_START = "Click to "
const PARTIAL_HEALING_MODE_TEXT_END = " Partial Healing Mode"
const PARTIAL_HEALING_MODE_NOTES_START = "You are "
const PARTIAL_HEALING_MODE_NOTES_MID = " in Partial Healing Mode. Click this button to "
const PARTIAL_HEALING_MODE_NOTES_END = " that Mode."

const PARTIAL_HEALING_MODE_TEXT_EXIT = PARTIAL_HEALING_MODE_TEXT_START + "exit" + PARTIAL_HEALING_MODE_TEXT_END
const PARTIAL_HEALING_MODE_TEXT_ENTER = PARTIAL_HEALING_MODE_TEXT_START + "enter" + PARTIAL_HEALING_MODE_TEXT_END

const PARTIAL_HEALING_MODE_NOTES_EXIT = PARTIAL_HEALING_MODE_NOTES_START + "currently" + PARTIAL_HEALING_MODE_NOTES_MID + "exit" + PARTIAL_HEALING_MODE_NOTES_END
const PARTIAL_HEALING_MODE_NOTES_ENTER = PARTIAL_HEALING_MODE_NOTES_START + "not currently" + PARTIAL_HEALING_MODE_NOTES_MID + "enter" + PARTIAL_HEALING_MODE_NOTES_END

const PARTIAL_HEALING_MODE_ALIAS = "PARTIAL_HEALING_TOGGLE"
const PARTIAL_HEALING_MODE_VALUE = "0"

// Button initializes assuming they are in Partial Healing Mode
const PARTIAL_HEALING_MODE_BUTTON = {
    "text": PARTIAL_HEALING_MODE_TEXT_EXIT,
    "type": "reward",
    "alias": PARTIAL_HEALING_MODE_ALIAS,
    "notes": PARTIAL_HEALING_MODE_NOTES_EXIT,
    "value": PARTIAL_HEALING_MODE_VALUE,
}

const HP_KEY = "HP_KEY"
const LVL_KEY = "LVL_KEY"
const PARTIAL_HEALING_MODE_ACTIVE_KEY = "PARTIAL_HEALING_MODE_ACTIVE_KEY"

var hpKey = ""
var lvlKey = ""
var partialHealingModeActiveKey = ""

function doOneTimeSetup() { 
  // Create button if user indicated they want it
  if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) {
    api_createNewTaskForUser([PARTIAL_HEALING_MODE_BUTTON])
  } 
  
  // create the webhook
  const options = {
    "scored" : true,
  }
  const payload = {
    "url" : WEB_APP_URL,
    "label" : SCRIPT_NAME + " Webhook",
    "type" : "taskActivity",
    "options" : options,
  }
  apiMult_createNewWebhookNoDuplicates(payload)
  
  // set script properties so they carry over to next session
  initScriptProperties()
}

// do things when the webhook runs
function doPost(e) {
  const dataContents = JSON.parse(e.postData.contents)
  const type = dataContents.type
  const task = dataContents.task

  if (type == "scored") {
    // Sanitize task alias
    let sanitizedAlias = "sanitized" // This will be the value if undefined, null, or blank
    if ( (task.alias != undefined) && (task.alias != null) && (task.alias != "") ) {
      sanitizedAlias = task.alias
    }
    
    var partialHealingModeActiveKey = PARTIAL_HEALING_MODE_ACTIVE_KEY
    var partialHealingModeActive = Number(scriptProperties.getProperty(partialHealingModeActiveKey))
    
    // If they press the toggle button, do so
    if (sanitizedAlias == PARTIAL_HEALING_MODE_ALIAS) {
      togglePartialHealingMode()
    } else { // If they didn't press the toggle button, do things as normal if they are in Partial Healing Mode
      if (partialHealingModeActive == 1) {
        var hpKey = HP_KEY
        var lvlKey = LVL_KEY

        var prevHp = Number(scriptProperties.getProperty(hpKey))
        var prevLvl = Number(scriptProperties.getProperty(lvlKey))

        const response = api_getAuthenticatedUserProfile("stats")
        user = JSON.parse(response).data

        let currentHp = user.stats.hp
        let currentLvl = user.stats.lvl
        
        // See if they've leveled up
        if (currentLvl > prevLvl) {
          // If yes, run correct version of Partial Healing
          if ( VERSION == 2 ) {
            // Value to heal must be between 0 and 50
            let valueToHeal = sanitizeInput(HP_SET_VALUE_PER_LEVEL, 0, 50)
            
            // New HP must be between 0 and 50
            let newHp = sanitizeInput(valueToHeal + prevHp,0, 50)
            
            // Heal the correct amount.
            api_updateUser({"stats.hp" : newHp})
            
            // Save new HP
            scriptProperties.setProperty(hpKey, newHp)
          }
          else if ( VERSION == 3 ) {
            // Percent to heal must be between 0 and 100
            let percentToHeal = sanitizeInput (HP_PERCENT_PER_LEVEL, 0, 100)
            let healing = ( percentToHeal / 100 ) * ( 50 - prevHp )
            
            // New HP must be between 0 and 50
            let newHpPercent = sanitizeInput(healing + prevHp,0, 50)

            // Heal the correct amount.
            api_updateUser({"stats.hp" : newHpPercent})
            
            // Save new HP
            scriptProperties.setProperty(hpKey, newHpPercent)
          }
          
          // Irrespective of version, save new level
          scriptProperties.setProperty(lvlKey, currentLvl)
        }
        // If they haven't leveled up since the last time the script ran...
        else {
          // If previous and current HP are the same, no need to re-save. Otherwise, do.
          if (prevHp != currentHp) {
            scriptProperties.setProperty(hpKey, currentHp)
          }
        }
      }
    }
  }
  return HtmlService.createHtmlOutput()
}

// Enters or exits from Partial Healing Mode
function togglePartialHealingMode(){
  var partialHealingModeActiveKey = PARTIAL_HEALING_MODE_ACTIVE_KEY
  var partialHealingModeActive = Number(scriptProperties.getProperty(partialHealingModeActiveKey))
  
  // If not in Partial Healing Mode, enter it.
  if (partialHealingModeActive == 0) {
    scriptProperties.setProperty(partialHealingModeActiveKey, 1)

    // Save HP and level
    initiatePartialHealingValues()
    
    // Update button if the user created it
    if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) {
      api_updateTask(PARTIAL_HEALING_MODE_ALIAS, {"text": PARTIAL_HEALING_MODE_TEXT_EXIT, "notes": PARTIAL_HEALING_MODE_NOTES_EXIT,})
    }
  } 
  // If in Partial Healing Mode, exit it.
  else if (partialHealingModeActive == 1) {
    scriptProperties.setProperty(partialHealingModeActiveKey, 0)
    
    // Update button if the user created it
    if (PARTIAL_HEALING_TOGGLE_BUTTON == 1) {
      api_updateTask(PARTIAL_HEALING_MODE_ALIAS, {"text": PARTIAL_HEALING_MODE_TEXT_ENTER, "notes": PARTIAL_HEALING_MODE_NOTES_ENTER,})
    }
  } 
}

// Create custom reward buttons
function api_createNewTaskForUser(payload) {
  var params = {
    "method" : "post",
    "headers" : HEADERS,
    "contentType" : "application/json",
    "payload" : JSON.stringify(payload), // Rightmost button goes on top
    "muteHttpExceptions" : true,
  }

  var url = "https://habitica.com/api/v3/tasks/user"
  UrlFetchApp.fetch(url, params)
}

// Create a webhook if no duplicate exists
function apiMult_createNewWebhookNoDuplicates(payload) {
  const response = api_getWebhooks()
  const webhooks = JSON.parse(response).data
  var duplicateExists = 0
    
  for (var i in webhooks) {
    if (webhooks[i].label == payload.label) {
      duplicateExists = 1
    }
  }
  // If webhook to be created doesn't exist yet
  if (!duplicateExists) {
    api_createNewWebhook(payload)
  }
}

// Used to see existing webhooks, and therefore if there's a duplicate
function api_getWebhooks() {
  const params = {
    "method" : "get",
    "headers" : HEADERS,
    "muteHttpExceptions" : true,
  }
  
  const url = "https://habitica.com/api/v3/user/webhook"
  return UrlFetchApp.fetch(url, params)
}

// Creates a webhook (as part of the "don't make it if there's a duplicate" function)
function api_createNewWebhook(payload) {
  const params = {
    "method" : "post",
    "headers" : HEADERS,
    "contentType" : "application/json",
    "payload" : JSON.stringify(payload),
    "muteHttpExceptions" : true,
  }
   
  const url = "https://habitica.com/api/v3/user/webhook"
  return UrlFetchApp.fetch(url, params)
}

// Gets user info so I can use it, especially stats like mana, experience, and level
function api_getAuthenticatedUserProfile(userFields) {
  const params = {
    "method" : "get",
    "headers" : HEADERS,
    "muteHttpExceptions" : true,
  }
  
  var url = "https://habitica.com/api/v3/user"
  if (userFields != "") {
    url += "?userFields=" + userFields
  }

  return UrlFetchApp.fetch(url, params)
}

// Sets initial properties that will be used/saved later.
function initScriptProperties() {
  scriptProperties.setProperty(PARTIAL_HEALING_MODE_ACTIVE_KEY, 1)

  // Also save HP and level; it's in a separate function on purpose.
  initiatePartialHealingValues()
}

// Saves initial HP and Level values
function initiatePartialHealingValues(){
  var hpKey = HP_KEY
  var lvlKey = LVL_KEY
  
  const responseInit = api_getAuthenticatedUserProfile("stats")
  user = JSON.parse(responseInit).data
    
  var currentHp = user.stats.hp
  var currentLvl = user.stats.lvl
  
  scriptProperties.setProperty(hpKey, currentHp)
  scriptProperties.setProperty(lvlKey, currentLvl)
}

// Ensures that the input is between the minimum and maximum. Can use "none" in either argument to indicate if there is either no minimum or no maximum.
function sanitizeInput(input, minimum, maximum) {
  if (minimum == "none"){ // Condition for if there is no minimum
    if ( input > maximum ) {
      return maximum
    } else { 
      return input
    }
  } else if (maximum == "none"){ // Condition for if there is no maximum
    if ( input < minimum ) {
      return minimum
    } else { 
      return input
    }
  } else { // Condition where there is both minimum and maximum
    if ( input < minimum ) {
      return minimum;
    } else if ( input > maximum ) {
      return maximum
    } else { 
      return input
    }
  }
}

function api_updateUser(payload) { 
  const params = {
    "method" : "put",
    "headers" : HEADERS,
    "contentType" : "application/json",
    "payload" : JSON.stringify(payload),
    "muteHttpExceptions" : true,
  }
  
  const url = "https://habitica.com/api/v3/user"
  return UrlFetchApp.fetch(url, params)
}

// Update a task
function api_updateTask(taskIdOrAlias, payload) {
  const params = {
    "method" : "put",
    "headers" : HEADERS,
    "contentType" : "application/json",
    "payload" : JSON.stringify(payload),
    "muteHttpExceptions" : true,
  }

  const url = "https://habitica.com/api/v3/tasks/" + taskIdOrAlias
  return UrlFetchApp.fetch(url, params)
}

// Send a notification as a private message regardless of if they're enabled
function api_sendPrivateMessageAlways(payload) {
  const params = {
    "method" : "post",
    "headers" : HEADERS,
    "contentType" : "application/json",
    "payload" : JSON.stringify(payload),
    "muteHttpExceptions" : true,
  }
  const url = "https://habitica.com/api/v3/members/send-private-message"
  return UrlFetchApp.fetch(url, params)
}

// FUNCTIONS FOR DEBUGGING. SCRIPT DOES NOT USE THEM, THEY MUST BE TRIGGERED MANUALLY

// In case the script saved the wrong value for HP and level, use FCV to set them to the correct amounts and then run the function below to save them
function debugResetSavedHpAndLevel() {
  const responseInit = api_getAuthenticatedUserProfile("stats")
  user = JSON.parse(responseInit).data
    
  let currentHp = user.stats.hp
  let currentLvl = user.stats.lvl
  
  scriptProperties.setProperty(HP_KEY, currentHp)
  scriptProperties.setProperty(LVL_KEY, currentLvl)
}

// Retrieves saved values and sends them in a private message
function debugGetSavedHpAndLevel(){
  let savedHp = Number(scriptProperties.getProperty(HP_KEY))
  let savedLvl = scriptProperties.getProperty(LVL_KEY)
  
  // saving these will make my life easier
  let MSG_JOINER_BEFORE = " is `"
  let MSG_JOINER_AFTER = "`, "
  
  api_sendPrivateMessageAlways({"message" : "Saved values are as follows: savedHp is `" + savedHp + MSG_JOINER_AFTER
                                + "savedLvl" + MSG_JOINER_BEFORE + savedLvl + "`", "toUserId" : USER_ID})
}

// Mannually edits and saves the selected value (in case it saved incorrectly)
function debugManuallyEditSavedValue() {
  // Fill them in below. savedHp is shown as a sample.
  let newValue = 0
  let key = HP_KEY
  
  // Save
  scriptProperties.setProperty(key, newValue)
}