class ZuoraConnect::AppInstanceBase

def self.decrypt_response(resp)

def self.decrypt_response(resp)
  OpenSSL::PKey::RSA.new(ZuoraConnect.configuration.private_key).private_decrypt(resp)
end

def self.get_metrics(type)

def self.get_metrics(type)
  namespace = ENV['DEIS_APP'].present? ? "#{ENV['DEIS_APP']}" : "#{Rails.application.class.parent_name}"
  @data = {}
  if type == "versions"
    @data = {
      app_name: namespace,
      url: "dummy",
      Version_Gem: ZuoraConnect::VERSION,
      Version_Zuora: ZuoraAPI::VERSION ,
      Version_Ruby: RUBY_VERSION,
      Version_Rails: Rails.version,
      hold: 1
    }
  elsif type == "stats"
    begin
      Resque.redis.ping
      @data = {
        app_name: namespace,
        url: "dummy",
        Resque:{
          Jobs_Finished: Resque.info[:processed] ,
          Jobs_Failed: Resque.info[:failed],
          Jobs_Pending: Resque.info[:pending],
          Workers_Active: Resque.info[:working],
          Workers_Total: Resque.info[:workers]
        }
      }
    rescue
    end
  end
  return @data
end

def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)

## START Aggregate Grouping Helping Methods ####
def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
  self.update_functions
  #Broke function into two parts to ensure transaction size was small enough
  ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Table\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
  ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Index\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)]) if index_table
end

def self.update_functions

def self.update_functions
  ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
end

def self.write_to_telegraf(*args)

### START Metrics Mathods ####
def self.write_to_telegraf(*args)
  if ZuoraConnect.configuration.enable_metrics
    @@telegraf_host = ZuoraConnect::Telegraf.new() if @@telegraf_host == nil
    return @@telegraf_host.write(*args)
  end
end

def apartment_switch(method = nil, migrate = false)

def apartment_switch(method = nil, migrate = false)
  begin
    Apartment::Tenant.switch!(self.id) if self.persisted?
  rescue Apartment::TenantNotFound => ex
    Apartment::Tenant.create(self.id.to_s)
    retry
  end
  if migrate && ActiveRecord::Migrator.needs_migration?
    Apartment::Migrator.migrate(self.id)
  end
  Thread.current[:appinstance] = self
end

def api_limit(start: true, time: API_LIMIT_TIMEOUT.to_i)

## START Resque Helping Methods ####
def api_limit(start: true, time: API_LIMIT_TIMEOUT.to_i)
  if start
    Redis.current.setex("APILimits:#{self.id}", time, true)
  else
    Redis.current.del("APILimits:#{self.id}")
  end
end

def api_limit?

def api_limit?
  return Redis.current.get("APILimits:#{self.id}").to_bool
end

def attr_builder(field,val)

def attr_builder(field,val)
  singleton_class.class_eval { attr_accessor "#{field}" }
  send("#{field}=", val)
end

def build_task(task_data: {}, session: {})

### START Task Mathods ####
def build_task(task_data: {}, session: {})
  self.task_data = task_data
  self.mode = self.task_data["mode"]
  self.task_data.each do |k,v|
    if k.match(/^(.*)_login$/)
      tmp = ZuoraConnect::Login.new(v)
      if v["tenant_type"] == "Zuora"
        if tmp.entities.size > 0
          tmp.entities.each do |value|
            entity_id = value["id"]
            tmp.client(entity_id).current_session          = session["#{self.id}::#{k}::#{entity_id}:current_session"]               if session["#{self.id}::#{k}::#{entity_id}:current_session"]
            tmp.client(entity_id).bearer_token             = session["#{self.id}::#{k}::#{entity_id}:bearer_token"]                  if session["#{self.id}::#{k}::#{entity_id}:bearer_token"]
            tmp.client(entity_id).oauth_session_expires_at = session["#{self.id}::#{k}::#{entity_id}:oauth_session_expires_at"]      if session["#{self.id}::#{k}::#{entity_id}:oauth_session_expires_at"]
          end
        else
          tmp.client.current_session                       = session["#{self.id}::#{k}:current_session"]                             if session["#{self.id}::#{k}:current_session"]
          tmp.client.bearer_token                          = session["#{self.id}::#{k}:bearer_token"]                                if session["#{self.id}::#{k}:bearer_token"] && tmp.client.respond_to?(:bearer_token) ## need incase session id goes from basic to aouth in same redis store
          tmp.client.oauth_session_expires_at              = session["#{self.id}::#{k}:oauth_session_expires_at"]                    if session["#{self.id}::#{k}:oauth_session_expires_at"]  && tmp.client.respond_to?(:oauth_session_expires_at) 
        end
        self.logins[k] = tmp
        self.attr_builder(k, @logins[k])
      end
    elsif k == "options"
      v.each do |opt|
        self.options[opt["config_name"]] = opt
      end
    elsif k == "user_settings"
      self.timezone =  v["timezone"]
      self.locale = v["local"]
    end
  end
rescue => ex
  Rails.logger.error("Task Data: #{task_data}")
  Rails.logger.error("Task Session: #{session.to_hash}")
  raise
end

def cache_app_instance

def cache_app_instance
  if defined?(Redis.current)
    #Task data must be present and the last refresh cannot be old. We dont want to overwite new cache data with old
    if self.task_data.present? &&  (self.last_refresh.to_i > INSTANCE_REFRESH_WINDOW.ago.to_i)
      Rails.logger.debug("[#{self.id}] Caching AppInstance")
      Redis.current.setex("AppInstance:#{self.id}", INSTANCE_REDIS_CACHE_PERIOD.to_i, encrypt_data(data: self.save_data))
    end
    Redis.current.del("Deleted:#{self.id}")
  end
end

def catalog_loaded?

def catalog_loaded?
  return ActiveRecord::Base.connection.execute('SELECT id FROM "public"."zuora_connect_app_instances" WHERE "id" = %s AND catalog = \'{}\' LIMIT 1' % [self.id]).first.nil?
end

def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)

cache: Store individual "1" object lookup in redis for caching.
child_objects: Whether to include child objects of the object in question.
object_id: The id or id's of the object/objects to be returned.
object: The Object class desired to be returned. Available [:product, :rateplan, :charge]
entity_id: If the using catalog json be field to store multiple entity product catalogs.
Catalog lookup provides method to lookup zuora catalog efficiently.
def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
  entity_reference = entity_id.blank? ? 'Default' : entity_id
  if object_id.present? && ![Array, String].include?(object_id.class)
    raise "Object Id can only be a string or an array of strings"
  end
  if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
    stub_catalog = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}"))
    object_hierarchy = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Hierarchy"))
  end
  if defined?(object_hierarchy)
    object_hierarchy ||= (JSON.parse(ActiveRecord::Base.connection.execute('SELECT catalog_mapping #> \'{%s}\' AS item FROM "public"."zuora_connect_app_instances" WHERE "id" = %s LIMIT 1' % [entity_reference, self.id]).first["item"] || "{}") [object_id] || {"productId" => "SAFTEY", "productRatePlanId" => "SAFTEY", "productRatePlanChargeId" => "SAFTEY"})
  end
  case object
  when :product
    if object_id.nil?
      string =
        "SELECT "\
          "json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
        "FROM "\
          "\"public\".\"zuora_connect_app_instances\", "\
          "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
        "WHERE "\
          "\"id\" = %s" % [entity_reference, self.id]
    else
      if object_id.class == String
        string =
          "SELECT "\
            "(catalog #> '{%s, %s}') #{child_objects ? '' : '- \'productRatePlans\''} AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\" "\
          "WHERE "\
            "\"id\" = %s" % [entity_reference, object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id, self.id]
      elsif object_id.class == Array
        string =
          "SELECT "\
            "json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\", "\
            "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
          "WHERE "\
            "\"product_id\" IN (\'%s\') AND "\
            "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
      end
    end
  when :rateplan
    if object_id.nil?
      string =
        "SELECT "\
          "json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
        "FROM "\
          "\"public\".\"zuora_connect_app_instances\", "\
          "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
          "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
        "WHERE "\
          "\"id\" = %s" % [entity_reference, self.id]
    else
      if object_id.class == String
        string =
          "SELECT "\
            "(catalog #> '{%s, %s, productRatePlans, %s}') #{child_objects ? '' : '- \'productRatePlanCharges\''} AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\" "\
          "WHERE "\
            "\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id,  self.id]
      elsif object_id.class == Array
        string =
          "SELECT "\
            "json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\", "\
            "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
            "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
          "WHERE "\
            "\"rateplan_id\" IN (\'%s\') AND "\
            "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
      end
    end
  when :charge
    if object_id.nil?
      string =
        "SELECT "\
          "json_object_agg(charge_id, charge) as item "\
        "FROM "\
          "\"public\".\"zuora_connect_app_instances\", "\
          "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
          "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
          "jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
        "WHERE "\
          "\"id\" = %s" % [entity_reference, self.id]
    else
      if object_id.class == String
        string =
          "SELECT "\
            "catalog #> '{%s, %s, productRatePlans, %s, productRatePlanCharges, %s}' AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\" "\
          "WHERE "\
            "\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_hierarchy['productRatePlanId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id , self.id]
      elsif object_id.class == Array
        string =
          "SELECT "\
            "json_object_agg(charge_id, charge) AS item "\
          "FROM "\
            "\"public\".\"zuora_connect_app_instances\", "\
            "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
            "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
            "jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
          "WHERE "\
            "\"charge_id\" IN (\'%s\') AND "\
            "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
      end
    end
  else
    raise "Available objects include [:product, :rateplan, :charge]"
  end
  stub_catalog ||= JSON.parse(ActiveRecord::Base.connection.execute(string).first["item"] || "{}")
  if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
    Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
    Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog))  if cache
  end
  return stub_catalog
end

def catalog_outdated?(time: Time.now - 12.hours)

def catalog_outdated?(time: Time.now - 12.hours)
  return self.catalog_updated_at.blank? || (self.catalog_updated_at < time)
end

def check_oauth_state(method)

### START Connect OAUTH methods ####
def check_oauth_state(method)
  #Refresh token if already expired
  if self.oauth_expired?
    Rails.logger.debug("[#{self.id}] Before '#{method}' method, Oauth expired")
    self.refresh_oauth
  end
end

def data_lookup(session: {})

def data_lookup(session: {})
  if defined?(Redis.current)
    cached_instance = Redis.current.get("AppInstance:#{self.id}")
    if cached_instance.blank?
      Rails.logger.debug("[#{self.id}] Cached AppInstance Missing")
      return session
    else
      Rails.logger.debug("[#{self.id}] Cached AppInstance Found")
      return decrypt_data(data: cached_instance, rescue_return: session).merge(session)
    end
  else
    return session
  end
end

def decrypt_data(data: nil, rescue_return: nil)

def decrypt_data(data: nil, rescue_return: nil)
  return data if data.blank?
  begin
    if Rails.env == 'development'
      return JSON.parse(data)
    else
      begin
        return JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(data)))
      rescue ActiveSupport::MessageVerifier::InvalidSignature => ex
        Rails.logger.fatal('Error Decrypting')
        return rescue_return
      end
    end
  rescue JSON::ParserError => ex
    Rails.logger.fatal('Error Parsing')
    return rescue_return
  end
end

def drop_instance

Method for overiding droping of an app instance
def drop_instance
  self.drop_message = 'Ok to drop'
  return true
end

def encrypt_data(data: nil)

def encrypt_data(data: nil)
  return data if data.blank?
  if Rails.env == 'development'
    return data.to_json
  else
    return encryptor.encrypt_and_sign(data.to_json)
  end
end

def encryptor

def encryptor
  # Default values for Rails 4 apps
  key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
  key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base, iterations: key_iter_num)
  secret, sign_secret = [key_generator.generate_key(salt), key_generator.generate_key(signed_salt)]
  return ActiveSupport::MessageEncryptor.new(secret, sign_secret)
end

def get_catalog(page_size: 5, zuora_login: self.login_lookup(type: "Zuora").first, entity_id: nil)

## START Catalog Helping Methods #####
def get_catalog(page_size: 5, zuora_login: self.login_lookup(type: "Zuora").first, entity_id: nil)
  self.update_column(:catalog_update_attempt_at, Time.now.utc)
  entity_reference = entity_id.blank? ? 'Default' : entity_id
  Rails.logger.debug("Fetch Catalog")
  Rails.logger.debug("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
  login = zuora_login.client(entity_reference)
  old_logger = ActiveRecord::Base.logger
  ActiveRecord::Base.logger = nil
  ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp}\', \'{}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp}\', \'{}\') where "id" = %{id}' % {:id => self.id})
  response = {'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{page_size}")}
  while !response["nextPage"].blank?
    url = login.rest_endpoint(response["nextPage"].split('/v1/').last)
    Rails.logger.debug("Fetch Catalog URL #{url}")
    output_json, response = login.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true)
    Rails.logger.debug("Fetch Catalog Response Code #{response.code}")
    if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
      Rails.logger.error("Fetch Catalog DATA #{output_json.to_json}")
      raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}")
    end
    output_json["products"].each do |product|
      ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [product["id"], {"productId" => product["id"]}.to_json.gsub("'", "''"), self.id])
      rateplans = {}
      product["productRatePlans"].each do |rateplan|
        ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [rateplan["id"],  {"productId" => product["id"], "productRatePlanId" => rateplan["id"]}.to_json.gsub("'", "''"), self.id])
        charges = {}
        rateplan["productRatePlanCharges"].each do |charge|
          ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [charge["id"],  {"productId" => product["id"], "productRatePlanId" => rateplan["id"], "productRatePlanChargeId" => charge["id"]}.to_json.gsub("'", "''"), self.id])
          charges[charge["id"]] = charge.merge({"productId" => product["id"], "productName" => product["name"], "productRatePlanId" => rateplan["id"], "productRatePlanName" => rateplan["name"] })
        end
        rateplan["productRatePlanCharges"] = charges
        rateplans[rateplan["id"]] = rateplan.merge({"productId" => product["id"], "productName" => product["name"]})
      end
      product["productRatePlans"] = rateplans
      ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp, %s}\', \'%s\') where "id" = %s' % [product["id"], product.to_json.gsub("'", "''"), self.id])
    end
  end
  # Move from tmp to actual
  ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{%{entity}}\', "catalog" #> \'{tmp}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{%{entity}}\',  "catalog_mapping" #> \'{tmp}\') where "id" = %{id}' % {:entity => entity_reference, :id => self.id})
  if defined?(Redis.current)
    Redis.current.keys("Catalog:#{self.id}:*").each do |key|
      Redis.current.del(key.to_s)
    end
  end
  # Clear tmp holder
  ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp}\', \'{}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp}\', \'{}\') where "id" = %{id}' % {:id => self.id})
  ActiveRecord::Base.logger = old_logger
  self.update_column(:catalog_updated_at, Time.now.utc)
  self.touch
  # DO NOT RETURN CATALOG. THIS IS NOT SCALABLE WITH LARGE CATALOGS. USE THE  CATALOG_LOOKUP method provided
  return true
end

def get_s3_file_url(key)

def get_s3_file_url(key)
  require 'aws-sdk-s3'
  signer = Aws::S3::Presigner.new(client: self.s3_client)
  url = signer.presigned_url(:get_object, bucket: ZuoraConnect.configuration.s3_bucket_name, key: "#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{key}")
end

def init

def init
  self.connect_user = 'Nobody'
  self.options = Hash.new
  self.logins = Hash.new
  self.api_version = "v2"
  self.attr_builder("timezone", ZuoraConnect.configuration.default_time_zone)
  self.attr_builder("locale", ZuoraConnect.configuration.default_locale)
  PaperTrail.whodunnit = "Backend" if defined?(PaperTrail)
  if INSTANCE_REFRESH_WINDOW > INSTANCE_REDIS_CACHE_PERIOD
    raise "The instance refresh window cannot be greater than the instance cache period"
  end
  self.apartment_switch(nil, true)
end

def instance_failure(failure)

def instance_failure(failure)
  raise failure
end

def login_lookup(type: "Zuora")

def login_lookup(type: "Zuora")
  results = []
  self.logins.each do |name, login|
    results << login if login.tenant_type == type
  end
  return results
end

def mark_for_refresh

def mark_for_refresh
  return defined?(Redis.current) ? Redis.current.set("AppInstance:#{self.id}:Refreshing", true, {:nx => true, :ex => REFRESH_TIMEOUT.to_i}) : true
end

def marked_for_refresh?

### START AppInstance Temporary Persistance Methods ####
def marked_for_refresh?
  return defined?(Redis.current) ? Redis.current.get("AppInstance:#{self.id}:Refreshing").to_bool : false
end

def method_missing(method_sym, *arguments, &block)

def method_missing(method_sym, *arguments, &block)
  if method_sym.to_s.include?("login")
    Rails.logger.fatal("Method Missing #{method_sym}")
    Rails.logger.fatal("Instance Data: #{self.task_data}")
    Rails.logger.fatal("Instance Logins: #{self.logins}")
  end
  super
end

def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token, holding_pattern: false, log_level: Logger::DEBUG)

def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token, holding_pattern: false, log_level: Logger::DEBUG)
  self.api_version = "v2"
  self.username = username
  self.password = password
  self.last_refresh = session["#{self.id}::last_refresh"]
  self.connect_user = session["#{self.id}::user::email"]
  PaperTrail.whodunnit = self.connect_user if defined?(PaperTrail)
  ## DEV MODE TASK DATA MOCKUP
  if ZuoraConnect.configuration.mode != "Production"
    mock_task_data = {
      "mode" => ZuoraConnect.configuration.dev_mode_mode
    }
    case ZuoraConnect.configuration.dev_mode_options.class
    when Hash
      self.options = ZuoraConnect.configuration.dev_mode_options
    when Array
      mock_task_data["options"] = ZuoraConnect.configuration.dev_mode_options
    end
    ZuoraConnect.configuration.dev_mode_logins.each do |k,v|
      v = v.merge({"entities": [] }) if !v.keys.include?("entities")
      mock_task_data[k] = v
    end
    self.build_task(task_data: mock_task_data, session: session)
  else
    time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
    if session.empty?
      self.new_session_message = "REFRESHING - Session Empty"
      raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
      self.refresh(session)
    elsif (self.id != session["appInstance"].to_i)
      self.new_session_message = "REFRESHING - AppInstance ID(#{self.id}) does not match session id(#{session["appInstance"].to_i})"
      raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
      self.refresh(session)
    elsif session["#{self.id}::task_data"].blank?
      self.new_session_message = "REFRESHING - Task Data Blank"
      raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
      self.refresh(session)
    elsif session["#{self.id}::last_refresh"].blank?
      self.new_session_message = "REFRESHING - No Time on Cookie"
      raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
      self.refresh(session)
    # If the cache is expired and we can aquire a refresh lock
    elsif (session["#{self.id}::last_refresh"].to_i < INSTANCE_REFRESH_WINDOW.ago.to_i) && self.mark_for_refresh
      self.new_session_message = "REFRESHING - Session Old by #{time_expire.abs} second"
      self.refresh(session)
    else
      if time_expire < 0
        self.new_session_message = ["REBUILDING - Expired by #{time_expire} seconds", self.marked_for_refresh? ? " cache updating as of #{self.reset_mark_refreshed_at} seconds ago" : nil].compact.join(',')
      else
        self.new_session_message = "REBUILDING - Expires in #{time_expire} seconds"
      end
      self.build_task(task_data: session["#{self.id}::task_data"], session: session)
    end
  end
  begin
    I18n.locale = self.locale
  rescue I18n::InvalidLocale => ex
    Rails.logger.debug("Invalid Locale: #{ex.message}")
  end
  Time.zone = self.timezone
  return self
rescue ZuoraConnect::Exceptions::HoldingPattern => ex
  while self.marked_for_refresh?
    Rails.logger.add(log_level, "Holding - Expires in #{self.reset_mark_expires_at}")
    sleep(5)
  end
  self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token])
  session = self.data_lookup(session: session)
  retry
ensure
  Rails.logger.add(log_level, self.new_session_message)
end

def new_session_for_api_requests(params: {})

This can be usefull for apps that dont need connect metadata call, or credentials, to operate for api requests
Overide this method to avoid the new session call for api requests that use the before filter authenticate_app_api_request.
def new_session_for_api_requests(params: {})
  return true
end

def new_session_for_ui_requests(params: {})

This can be usefull for apps that dont need connect metadata call, or credentials, to operate for ui requests
Overide this method to avoid the new session call for ui requests that use the before filter authenticate_connect_app_request.
def new_session_for_ui_requests(params: {})
  return true
end

def oauth_expired?

def oauth_expired?
  return self.oauth_expires_at.present? ? (self.oauth_expires_at < Time.now) : true
end

def queue_pause(time: nil, current_user: 'Default')

def queue_pause(time: nil, current_user: 'Default')
  if time.present?
    raise "Time must be fixnum of seconds." if time.class != Fixnum
    Redis.current.setex("resque:PauseQueue:#{self.id}", time, current_user)
  else
    Redis.current.set("resque:PauseQueue:#{self.id}", current_user)
  end
end

def queue_paused?

def queue_paused?
  return Redis.current.get("resque:PauseQueue:#{self.id}").present?
end

def queue_start(current_user: 'Default')

def queue_start(current_user: 'Default')
  paused_user = Redis.current.get("resque:PauseQueue:#{self.id}") 
  if paused_user == current_user || paused_user.blank?
    Redis.current.del("resque:PauseQueue:#{self.id}")
  else
    raise "Can only unpause for user #{paused_user}." 
  end
end

def refresh(session = nil)

def refresh(session = nil)
  refresh_count ||= 0
  start = Time.now
  response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
  response_time = Time.now - start
  Rails.logger.debug("[#{self.id}] REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}")
  if response.code == 200
    self.build_task(task_data: JSON.parse(response.body), session: session)
    self.last_refresh = Time.now.to_i
    self.cache_app_instance
    self.reset_mark_for_refresh
  else
    Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed Code #{response.code}")
    raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
  end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
  if (refresh_count += 1) < 3
    Rails.logger.info("[#{self.id}] REFRESH TASK - #{ex.class} Retrying(#{refresh_count})")
    retry
  else
    Rails.logger.fatal("[#{self.id}] REFRESH TASK - #{ex.class} Failed #{refresh_count}x")
    raise
  end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
  if (refresh_count += 1) < 3
    Rails.logger.info("[#{self.id}] REFRESH TASK - Failed Retrying(#{refresh_count})")
    if ex.code == 401
      self.refresh_oauth
    end
    retry
  else
    Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed #{refresh_count}x")
    raise
  end
end

def refresh_oauth

def refresh_oauth
  refresh_oauth_count ||= 0
  start = Time.now
  params = {
            :grant_type => "refresh_token",
            :redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
            :refresh_token => self.refresh_token
          }
  response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
  response_time = Time.now - start
  Rails.logger.info("[#{self.id}] REFRESH OAUTH - In #{response_time.round(2).to_s}")
  if response.code == 200
    response_body = JSON.parse(response.body)
    self.refresh_token = response_body["refresh_token"]
    self.access_token = response_body["access_token"]
    self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
    self.save(:validate => false)
  else
    Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed Code #{response.code}")
    raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
  end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
  if (refresh_oauth_count += 1) < 3
    Rails.logger.info("[#{self.id}] REFRESH OAUTH - #{ex.class} Retrying(#{refresh_oauth_count})")
    retry
  else
    Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - #{ex.class} Failed #{refresh_oauth_count}x")
    raise
  end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
  sleep(5)
  self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token]) #Reload only the refresh token for retry
  #After reload, if nolonger expired return
  return if !self.oauth_expired?
  if (refresh_oauth_count += 1) < 3
    Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
    retry
  else
    Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
    raise
  end
end

def reload_attributes(selected_attributes)

def reload_attributes(selected_attributes)
  raise "Attibutes must be array" if selected_attributes.class != Array
  value_attributes = self.class.unscoped.where(:id=>id).select(selected_attributes).first.attributes
  value_attributes.each do |key, value|
    next if key == "id" && value.blank?
    self.send(:write_attribute, key, value)
  end
  return self
end

def reset_mark_expires_at

def reset_mark_expires_at
  return defined?(Redis.current) ? Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
end

def reset_mark_for_refresh

def reset_mark_for_refresh
  Redis.current.del("AppInstance:#{self.id}:Refreshing") if defined?(Redis.current)
end

def reset_mark_refreshed_at

def reset_mark_refreshed_at
  return defined?(Redis.current) ? REFRESH_TIMEOUT.to_i - Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
end

def s3_client

## START S3 Helping Methods #####
def s3_client
  require 'aws-sdk-s3'
  if ZuoraConnect.configuration.mode == "Development"
    @s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region,access_key_id: ZuoraConnect.configuration.dev_mode_access_key_id,secret_access_key: ZuoraConnect.configuration.dev_mode_secret_access_key)
  else
    @s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region)
  end
end

def save_data(session = Hash.new)

def save_data(session = Hash.new)
  self.logins.each do |key, login|
    if login.tenant_type == "Zuora"
      if login.available_entities.size > 1 && Rails.application.config.session_store != ActionDispatch::Session::CookieStore
        login.available_entities.each do |entity_key|
          session["#{self.id}::#{key}::#{entity_key}:current_session"]            = login.client(entity_key).current_session            if login.client.respond_to?(:current_session)
          session["#{self.id}::#{key}::#{entity_key}:bearer_token"]               = login.client(entity_key).bearer_token               if login.client.respond_to?(:bearer_token)
          session["#{self.id}::#{key}::#{entity_key}:oauth_session_expires_at"]   = login.client(entity_key).oauth_session_expires_at   if login.client.respond_to?(:oauth_session_expires_at)
        end
      else
        session["#{self.id}::#{key}:current_session"]             = login.client.current_session            if login.client.respond_to?(:current_session)
        session["#{self.id}::#{key}:bearer_token"]                = login.client.bearer_token               if login.client.respond_to?(:bearer_token)
        session["#{self.id}::#{key}:oauth_session_expires_at"]    = login.client.oauth_session_expires_at   if login.client.respond_to?(:oauth_session_expires_at)
      end
    end
  end
  session["#{self.id}::task_data"] = self.task_data
  #Redis is not defined strip out old data
  if !defined?(Redis.current)
    session["#{self.id}::task_data"].delete('applications')
    session["#{self.id}::task_data"].select {|k,v| k.include?('login') && v['tenant_type'] == 'Zuora'}.each do |login_key, login_data|
      session["#{self.id}::task_data"][login_key]['entities'] = (login_data.dig('entities') || []).map {|entity| entity.slice('id', 'tenantId')}
    end
  end
  session["#{self.id}::last_refresh"] = self.last_refresh
  session["appInstance"] = self.id
  return session
end

def send_email

def send_email
end

def updateOption(optionId, value)

def updateOption(optionId, value)
  response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
end

def update_logins(options)

EXAMPLE: {"name": "ftp_login_14","username": "ftplogin7","tenant_type": "Custom","password": "test2","url": "www.ftp.com","custom_data": { "path": "/var/usr/test"}}
This can update an existing login, add a new login, change to another existing login
def update_logins(options)
  update_login_count ||= 0
  response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
  parsed_json =  JSON.parse(response.body)
  if response.code == 200
    if defined?(Redis.current)
      self.build_task(task_data: parsed_json, session: self.data_lookup)
      self.last_refresh = Time.now.to_i
      self.cache_app_instance
    end
    return parsed_json
  elsif response.code == 400
    raise ZuoraConnect::Exceptions::APIError.new(message: parsed_json['errors'].join(' '), response: response.body, code: response.code)
  else
    raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
  end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
  if (update_login_count += 1) < 3
    retry
  else
    raise
  end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
  if (update_login_count += 1) < 3
    if ex.code == 401
      self.refresh_oauth
    end
    retry
  else
    raise
  end
end

def upload_to_s3(local_file,s3_path = nil)

def upload_to_s3(local_file,s3_path = nil)
  s3_path = local_file.split("/").last if s3_path.nil?
  obj = self.s3_client.bucket(ZuoraConnect.configuration.s3_bucket_name).object("#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{s3_path}}")
  obj.upload_file(local_file, :server_side_encryption => 'AES256')
end