local tablex = require("core.tablex")
local attributeUtil = require("ui.attributeUtil")
local consts = require("ui.consts")
local pickupConsts = require("ui.pickupConsts")
local pickupUtil = require("ui.pickupUtil")
local recipeUtil = require("ui.recipeUtil")
local resourceUtil = require("ui.resourceUtil")
local skillTreeConsts = require("ui.skillTreeConsts")
local util = require("ui.util")
local resourceConsts = require("ui.resourceConsts")
local UI = game.UI
local GetLevelName = function(skillRecipeName, level)
  return skillRecipeName .. "_Level0" .. level
end
local IsLevelAvailable = function(pickupName, skillLevelRecipeName)
  return recipeUtil.AtPickupTier(pickupName, skillLevelRecipeName)
end
local GetSkillLevel = function(skillRecipeName)
  local primaryItem = recipeUtil.GetPrimaryRecipeItem(skillRecipeName)
  local resourceName = recipeUtil.GetRecipeItemName(primaryItem)
  local skillLevel = 0
  for index = 1, skillTreeConsts.MAX_LEVEL_COUNT do
    local skillLevelRecipeName = GetLevelName(skillRecipeName, index)
    if resourceUtil.HasResource(skillLevelRecipeName) == true then
      skillLevel = index
    else
      break
    end
  end
  return skillLevel
end
local GetSkillMaxLevel = function(skillRecipeName)
  local primaryItem = recipeUtil.GetPrimaryRecipeItem(skillRecipeName)
  local resourceName = recipeUtil.GetRecipeItemName(primaryItem)
  return resourceUtil.GetMax(resourceName) - 1
end
local IsShieldSkill = function(skillRecipeName)
  return tablex.Contains(skillTreeConsts.Skills[pickupConsts.Shield], skillRecipeName)
end
local GetSkillBranch = function(skillRecipeName)
  local branchName = "Branch_" .. skillRecipeName
  if skillTreeConsts.BranchGONames[branchName] ~= nil then
    return branchName
  end
  return nil
end
local GetSkillType = function(skillRecipeName)
  assert(skillRecipeName ~= nil and type(skillRecipeName) == "string", "Invalid argument passed to GetSkillRecipeType")
  if IsShieldSkill(skillRecipeName) then
    return pickupConsts.Shield
  end
  if recipeUtil.HasFlag(skillRecipeName, skillTreeConsts.TweaksFlags[pickupConsts.Axe]) == true then
    return pickupConsts.Axe
  end
  if recipeUtil.HasFlag(skillRecipeName, skillTreeConsts.TweaksFlags[pickupConsts.Blades]) == true then
    return pickupConsts.Blades
  end
  if recipeUtil.HasFlag(skillRecipeName, skillTreeConsts.TweaksFlags[pickupConsts.SonBow]) == true then
    return pickupConsts.SonBow
  end
  assert(false, "Undefined/Invalid skill recipe passed to GetSkillRecipeType")
end
local GetAllLevelRecipes = function(skillRecipeName)
  local primaryItem = recipeUtil.GetPrimaryRecipeItem(skillRecipeName)
  local resourceName = recipeUtil.GetRecipeItemName(primaryItem)
  local maxSkillLevel = GetSkillMaxLevel(resourceName)
  local recipeTable = {}
  local levelRecipeIndex = 1
  for index = 1, maxSkillLevel do
    local skillLevelRecipeName = GetLevelName(skillRecipeName, index)
    tablex.FastInsert(recipeTable, skillLevelRecipeName, levelRecipeIndex)
    levelRecipeIndex = levelRecipeIndex + 1
  end
  return recipeTable
end
local IsSkillLevel = function(skillRecipeName)
  local isLevel = false
  if resourceUtil.HasResource(skillRecipeName) == true then
    local maxSkillLevel = GetSkillMaxLevel(skillRecipeName)
    if 0 < maxSkillLevel then
      local currSkillLevel = GetSkillLevel(skillRecipeName)
      local nextSkillLevel = util.Clamp(currSkillLevel + 1, 1, maxSkillLevel)
      isLevel = true
    end
  end
  return isLevel
end
local GetActiveRecipe = function(skillRecipeName)
  local recipe = skillRecipeName
  local isLevel = false
  if resourceUtil.HasResource(skillRecipeName) == true then
    local maxSkillLevel = GetSkillMaxLevel(skillRecipeName)
    if 0 < maxSkillLevel then
      local currSkillLevel = GetSkillLevel(skillRecipeName)
      local nextSkillLevel = util.Clamp(currSkillLevel + 1, 1, maxSkillLevel)
      recipe = GetLevelName(skillRecipeName, nextSkillLevel)
      isLevel = true
    end
  end
  return recipe, isLevel
end
local GetAttributeBonusesForSkill = function(skillRecipeName)
  local bonuses = {}
  local currentBonusInfo, currentBonusFlag
  for bonusFlag, infoTable in pairs(pickupConsts.AttributeBonuses) do
    if recipeUtil.HasFlag(skillRecipeName, bonusFlag) then
      local newBonus = {Info = nil, Flag = nil}
      newBonus.Info = infoTable
      newBonus.Flag = bonusFlag
      tablex.FastInsert(bonuses, newBonus, #bonuses + 1)
    end
  end
  return bonuses
end
local HasAnyActiveAttributeBonuses = function(skillRecipeName)
  local bonusTable = GetAttributeBonusesForSkill(skillRecipeName)
  for _, bonus in ipairs(bonusTable) do
    local requiredAttribute = bonus.Info.AttributeCondition
    local attributeString = attributeUtil.GetDisplayName(requiredAttribute)
    local currentAmount = attributeUtil.GetCurrentlyEquippedAttributeOnCharacter(pickupConsts.TAG_PICKUP_KRATOS, requiredAttribute)
    local requiredAmount = bonus.Info.ConditionAmount
    local hasEnough = currentAmount >= requiredAmount
    if hasEnough then
      return true
    end
  end
  return false
end
local HasEnoughResourcesToPurchase = function(skillRecipeName)
  local inputItems = recipeUtil.GetInputRecipeItems(skillRecipeName)
  local hasEnough = true
  for _, item in ipairs(inputItems) do
    local currResourceAmount = resourceUtil.GetAmount(item.Name)
    local requiredAmount = item.Amount
    if currResourceAmount < requiredAmount then
      hasEnough = false
      break
    end
  end
  return hasEnough
end
local IsSkillLockedByReinforcement = function(pickupName, skillRecipeName)
  local isLocked = not recipeUtil.AtPickupTier(pickupName, skillRecipeName)
  local _, requiredReinforcementLevel = recipeUtil.GetTier(skillRecipeName)
  requiredReinforcementLevel = requiredReinforcementLevel - 1
  if pickupName == pickupConsts.Shield then
    isLocked = false
    requiredReinforcementLevel = -1
  end
  return isLocked, requiredReinforcementLevel
end
local IsSkillLockedByResourceRequirement = function(skillRecipeName)
  assert(skillRecipeName ~= nil, "Skill recipe name passed into IsSkillLockedByResourceRequirement was nil")
  local isLocked = false
  local lockedResourceFlags = skillTreeConsts.LockedResourceFlags
  local requiredResourceNamesTable
  for recipeFlag, resourceNameTable in pairs(lockedResourceFlags) do
    if recipeUtil.HasFlag(skillRecipeName, recipeFlag) then
      local hasAllRequiredResources = true
      for _, resourceName in ipairs(resourceNameTable) do
        if not resourceUtil.HasResource(resourceName) then
          hasAllRequiredResources = false
          break
        end
      end
      if not hasAllRequiredResources then
        isLocked = true
        requiredResourceNamesTable = resourceNameTable
      end
      break
    end
  end
  return isLocked, requiredResourceNamesTable
end
local IsSkillLockedByPrerequisiteSkill = function(skillName)
  local inputItems = recipeUtil.GetInputRecipeItems(skillName)
  local requiredItems = {}
  local haveRequiredAmount = true
  for _, item in ipairs(inputItems) do
    local isConsumable = item.Consume
    if not isConsumable then
      local currResourceAmount = resourceUtil.GetAmount(item.Name)
      local requiredAmount = item.Amount
      if currResourceAmount < requiredAmount then
        haveRequiredAmount = false
        tablex.FastInsert(requiredItems, item, #requiredItems + 1)
      end
    end
  end
  return not haveRequiredAmount, requiredItems
end
local IsSkillPurchased = function(skillRecipeName)
  local skillIsPurchased = true
  local recipeOutputs = recipeUtil.GetOutputRecipeItems(skillRecipeName)
  for _, outputResource in ipairs(recipeOutputs) do
    if not resourceUtil.HasResource(outputResource.Name) then
      skillIsPurchased = false
      break
    end
  end
  return skillIsPurchased
end
local CanSkillBePurchased = function(pickupName, skillRecipeName)
  assert(type(pickupName) == "string", "PickupName passed to SkillUtil::CanSkillBePurchased was not a string. Value is " .. tostring(pickupName))
  assert(type(skillRecipeName) == "string", "skillRecipeName passed to SkillUtil::CanSkillBePurchased was not a string. Value is " .. tostring(skillRecipeName))
  if not recipeUtil.IsInWallet(resourceConsts.WALLET_KRATOS, skillRecipeName) then
    return false
  end
  if not resourceUtil.HasResource(pickupName) then
    return false
  end
  if IsSkillLockedByReinforcement(pickupName, skillRecipeName) then
    return false
  end
  if IsSkillLockedByResourceRequirement(skillRecipeName) then
    return false
  end
  if IsSkillLockedByPrerequisiteSkill(skillRecipeName) then
    return false
  end
  if IsSkillPurchased(skillRecipeName) then
    return false
  end
  if not recipeUtil.CanPurchase(skillRecipeName) then
    return false
  end
  if recipeUtil.AtLimit(skillRecipeName) then
    return false
  end
  return true
end
local SkillsAreAvailableForPurchase = function()
  for pickupName, skillTable in pairs(skillTreeConsts.Skills) do
    local hasWeapon = resourceUtil.HasResource(pickupName)
    if hasWeapon then
      for _, skillRecipeName in pairs(skillTable) do
        if CanSkillBePurchased(pickupName, skillRecipeName) then
          return true
        end
      end
    end
  end
  return false
end
local HasBonusRequirement = function(character, bonusFlag)
  local bonusInfo = pickupConsts.AttributeBonuses[bonusFlag]
  local currAttributes = attributeUtil.GetUICreatureAttributes(character)
  local attributeToCheck = bonusInfo.AttributeCondition
  local amountRequired = bonusInfo.ConditionAmount
  for i, _ in ipairs(pickupConsts.Attributes) do
    if attributeToCheck == pickupConsts.Attributes[i] then
      return amountRequired <= currAttributes[i]
    end
  end
  assert(false, "HasBonusRequirements failed because no attribute was found for bonus: " .. tostring(bonusFlag))
end
function GetNodeGOFromSkillRecipeName(skillRecipeName)
  local goButton = util.GetUiObjByName(skillRecipeName)
  goButton:Show()
  return goButton.Child
end
local g_skillTreeNodesPreviouslyLocked = {}
local UpdateSkillTreeLockedStates = function()
  for weaponName, weaponSkills in pairs(skillTreeConsts.Skills) do
    for _, skillRecipeName in ipairs(weaponSkills) do
      if IsSkillLockedByReinforcement(weaponName, skillRecipeName) then
        g_skillTreeNodesPreviouslyLocked[skillRecipeName] = true
      end
    end
  end
end
local WasPreviouslyLocked = function(skillRecipeName)
  return g_skillTreeNodesPreviouslyLocked[skillRecipeName]
end
local MarkSkillAsLocked = function(skillRecipeName)
  g_skillTreeNodesPreviouslyLocked[skillRecipeName] = true
end
local MarkSkillAsUnlocked = function(skillRecipeName)
  g_skillTreeNodesPreviouslyLocked[skillRecipeName] = false
end
local GetSkillList = function(pickupName)
  return skillTreeConsts.Skills[pickupName]
end
local GetSubStateNames = function()
  local subStateNames = {}
  for _, substateName in ipairs(consts.SUB_STATES.SkillTree) do
    local isBladesSelection = substateName == pickupConsts.Blades
    local hasBlades = resourceUtil.HasResource(pickupConsts.Blades)
    local isSonSelection = substateName == pickupConsts.SonBow
    if util.IsMenuSelectionAvailable(isBladesSelection, hasBlades, isSonSelection) then
      tablex.FastInsert(subStateNames, substateName, #subStateNames + 1)
    end
  end
  return subStateNames
end
local UpdateNewIcon = function(skillRecipeName, shouldShow)
  local goButton = util.GetUiObjByName(skillRecipeName)
  local goIcon = goButton.Child:FindSingleGOByName("newIcon")
  if goIcon ~= nil then
    local goIconChild = goIcon.Child
    if goIconChild ~= nil and shouldShow then
      goIcon:Show()
      UI.Anim(goIconChild, consts.AS_ForwardCycle_NoReset, "", 1)
    else
      goIcon:Hide()
    end
  end
end
function ShouldCheckNew(pickupName, skillRecipeName)
  local shouldCheck = true
  if not IsSkillPurchased(skillRecipeName) then
    local isBowLockedByArrow = pickupName == pickupConsts.SonBow and IsSkillLockedByResourceRequirement(skillRecipeName)
    local isLocked = IsSkillLockedByReinforcement(pickupName, skillRecipeName) or isBowLockedByArrow
    shouldCheck = not isLocked
  end
  return shouldCheck
end
function IsNodeNew(pickupName, skillRecipeName)
  local isNew = false
  if ShouldCheckNew(pickupName, skillRecipeName) then
    isNew = UI.HasNotification(consts.NotificationTypes.Recipe, skillRecipeName)
  end
  return isNew
end
function AnyNewNodesByWeapon(pickupName)
  local anyNew = false
  if pickupUtil.IsValidName(pickupName) then
    local skills = GetSkillList(pickupName)
    for _, skillRecipeName in ipairs(skills) do
      if IsNodeNew(pickupName, skillRecipeName) then
        anyNew = true
        break
      end
    end
  end
  return anyNew
end
local AnyNewInside = function()
  local subStateNames = GetSubStateNames()
  local anyNew = false
  for _, subState in ipairs(subStateNames) do
    if AnyNewNodesByWeapon(subState) then
      anyNew = true
      break
    end
  end
  return anyNew
end
return {
  GetLevelName = GetLevelName,
  IsLevelAvailable = IsLevelAvailable,
  GetSkillLevel = GetSkillLevel,
  GetSkillMaxLevel = GetSkillMaxLevel,
  GetSkillBranch = GetSkillBranch,
  GetSkillType = GetSkillType,
  GetAllLevelRecipes = GetAllLevelRecipes,
  GetActiveRecipe = GetActiveRecipe,
  GetAttributeBonusesForSkill = GetAttributeBonusesForSkill,
  HasAnyActiveAttributeBonuses = HasAnyActiveAttributeBonuses,
  HasEnoughResourcesToPurchase = HasEnoughResourcesToPurchase,
  IsSkillLockedByReinforcement = IsSkillLockedByReinforcement,
  IsSkillLockedByResourceRequirement = IsSkillLockedByResourceRequirement,
  IsSkillLockedByPrerequisiteSkill = IsSkillLockedByPrerequisiteSkill,
  CanSkillBePurchased = CanSkillBePurchased,
  IsSkillPurchased = IsSkillPurchased,
  SkillsAreAvailableForPurchase = SkillsAreAvailableForPurchase,
  HasBonusRequirement = HasBonusRequirement,
  GetNodeGOFromSkillRecipeName = GetNodeGOFromSkillRecipeName,
  UpdateSkillTreeLockedStates = UpdateSkillTreeLockedStates,
  WasPreviouslyLocked = WasPreviouslyLocked,
  MarkSkillAsLocked = MarkSkillAsLocked,
  MarkSkillAsUnlocked = MarkSkillAsUnlocked,
  GetSkillList = GetSkillList,
  GetSubStateNames = GetSubStateNames,
  UpdateNewIcon = UpdateNewIcon,
  ShouldCheckNew = ShouldCheckNew,
  IsNodeNew = IsNodeNew,
  AnyNewNodesByWeapon = AnyNewNodesByWeapon,
  AnyNewInside = AnyNewInside
}
