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.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 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)
  @task_data = task_data
  @mode = @task_data["mode"]
  @task_data.each do |k,v|
    if k.match(/^(.*)_login$/)
      tmp = ZuoraConnect::Login.new(v)
      if !session.nil? && 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}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}::#{entity_id}:session"]
          end
        else
          tmp.client.current_session = session["#{self.id}::#{k}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}:session"]
        end
      end
      @logins[k] = tmp
      self.attr_builder(k, @logins[k])
    elsif k == "options"
      v.each do |opt|
        @options[opt["config_name"]] = opt
      end
    elsif k == "user_settings"
      self.timezone =  v["timezone"]
      self.locale = v["local"]
    end
  end
  Thread.current[:appinstance] = self
end

def cache_app_instance

def cache_app_instance
  if defined?(Redis.current)
    if self.save_data["#{self.id}::task_data"].present?
      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?(PaperTrail)
    PaperTrail.whodunnit = session["#{self.id}::user::email"].present? ? session["#{self.id}::user::email"] : nil if session.present?
  end
  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: {})
    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 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.info("Fetch Catalog")
  Rails.logger.info("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
  @options = Hash.new
  @logins = Hash.new
  @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)
  begin 
    Apartment::Tenant.switch!(self.id) if self.persisted?
  rescue Apartment::TenantNotFound => ex
    Apartment::Tenant.create(self.id.to_s)
    retry
  end
  if(ActiveRecord::Migrator.needs_migration?)
    Apartment::Migrator.migrate(self.id)
  end
  if INSTANCE_REFRESH_WINDOW > INSTANCE_REDIS_CACHE_PERIOD
    raise "The instance refresh window cannot be greater than the instance cache period"
  end
  Thread.current[:appinstance] = self
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)

def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token)
  @api_version = "v2"
  @username = username
  @password = password
  @last_refresh = session["#{self.id}::last_refresh"]
  ## 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
      @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
    build_task(mock_task_data, session)
  else
    time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
    if session.empty?
      Rails.logger.info("[#{self.id}] REFRESHING - Session Empty")
      self.refresh(session)
    elsif (self.id != session["appInstance"].to_i) 
      Rails.logger.info("[#{self.id}] REFRESHING - AppInstance ID(#{self.id}) does not match session id(#{session["appInstance"].to_i})")
      self.refresh(session)
    elsif session["#{self.id}::task_data"].blank?
      Rails.logger.info("[#{self.id}] REFRESHING - Task Data Blank")
      self.refresh(session)
    elsif session["#{self.id}::last_refresh"].blank?
      Rails.logger.info("[#{self.id}] REFRESHING - No Time on Cookie") 
      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
      Rails.logger.info("[#{self.id}] REFRESHING - Session Old by #{time_expire.abs} second")
      self.refresh(session)
    else
      if time_expire < 0
        Rails.logger.info(["[#{self.id}] REBUILDING - Expired by #{time_expire} seconds", self.marked_for_refresh? ? " cache updating as of #{self.refreshed_at} seconds ago" : nil].compact.join(','))
      else
        Rails.logger.info("[#{self.id}] REBUILDING - Expires in #{time_expire} seconds")
      end
      build_task(session["#{self.id}::task_data"], session)
    end
  end
  begin
    I18n.locale = self.locale
  rescue I18n::InvalidLocale => ex
    Rails.logger.error("Invalid Locale: #{ex.message}")
  end
  Time.zone = self.timezone
  Thread.current[:appinstance] = self
  return self
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?
  (self.oauth_expires_at < Time.now)
end

def queue_pause(time: nil)

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

def queue_paused?

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

def queue_start

def queue_start
  Redis.current.del("resque:PauseQueue:#{self.id}")
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.info("[#{self.id}] REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}")
  if response.code == 200
    @last_refresh = Time.now.to_i
    build_task(JSON.parse(response.body), session)
  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
  if (refresh_count += 1) < 3
    retry
  else
    raise
  end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
  if (refresh_count += 1) < 3
    if ex.code == 401
      self.refresh_oauth
    end
    retry
  else
    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
    retry
  else
    raise
  end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
  if (refresh_oauth_count += 1) < 3
    Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
    sleep(5)
    self.reload_attributes([:refresh_token]) #Refload only the refresh token for retry
    retry
  else
    Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
    raise
  end
end

def refreshed_at

def refreshed_at
  return defined?(Redis.current) ? REFRESH_TIMEOUT.to_i - Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
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_for_refresh

def reset_mark_for_refresh
  Redis.current.del("AppInstance:#{self.id}:Refreshing") if defined?(Redis.current)
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}:session"] = login.client(entity_key).current_session
        end
      else
        session["#{self.id}::#{key}:session"] = login.client.current_session
      end
    end
  end
  session["#{self.id}::task_data"] = self.task_data
  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)
  return 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))
  if response.code == 200
    return JSON.parse(response.body)
  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
  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