class RemoteStorageAuthorization < ApplicationRecord
  belongs_to :user
  belongs_to :web_app, class_name: "AppCatalog::WebApp", optional: true

  serialize :permissions unless Rails.env.production?

  validates_presence_of :permissions
  validates_presence_of :client_id

  scope :valid, -> { where(expire_at: nil).or(where(expire_at: (DateTime.now)..)) }
  scope :expired, -> { where(expire_at: ..(DateTime.now)) }

  after_initialize do |a|
    a.permissions = [] if a.permissions == nil
  end

  before_create  :generate_token
  before_create  :store_token_in_redis
  before_create  :find_or_create_web_app
  after_create   :schedule_token_expiry
  after_create   :notify_user
  before_destroy :delete_token_from_redis
  after_destroy  :remove_token_expiry_job

  def url
    uri = URI.parse self.redirect_uri
    "#{uri.scheme}://#{client_id}"
  end

  def launch_url
    return url unless web_app && web_app.metadata[:start_url].present?

    start_url = web_app.metadata[:start_url]

    if start_url.match("^https?:\/\/")
      return start_url.start_with?(url) ? start_url : url
    else
      path = start_url.gsub(/^\.\.\//, "").gsub(/^\.\//, "").gsub(/^\//, "")
      "#{url}/#{path}"
    end
  end

  def delete_token_from_redis
    key = "authorizations:#{user.cn}:#{token}"
    redis.srem? key, redis.smembers(key)
  rescue => e
    Rails.logger.error e
    Sentry.capture_exception(e) if Setting.sentry_enabled?
  end

  private

    def redis
      @redis ||= Redis.new(url: Setting.rs_redis_url)
    end

    def generate_token(length=16)
      self.token = SecureRandom.hex(length) if self.token.blank?
    end

    def store_token_in_redis
      redis.sadd "authorizations:#{user.cn}:#{token}", permissions
    end

    def schedule_token_expiry
      return unless expire_at.present?
      RemoteStorageExpireAuthorizationJob.set(wait_until: expire_at)
                                         .perform_later(id)
    end

    def remove_token_expiry_job
      queue = Sidekiq::Queue.new(RemoteStorageExpireAuthorizationJob.queue_name)
      queue.each do |job|
        next unless job.display_class == "RemoteStorageExpireAuthorizationJob"
        job.delete if job.display_args == [id]
      end
    end

    def find_or_create_web_app
      if looks_like_hosted_origin?
        web_app = AppCatalog::WebApp.find_or_create_by!(url: self.url)
        web_app.update_metadata unless web_app.name.present?
        self.web_app = web_app
        self.app_name = web_app.name.presence || client_id
      else
        self.app_name = client_id
      end
    end

    def looks_like_hosted_origin?
      uri = URI.parse self.redirect_uri
      !!(uri.host =~ /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/)
    rescue URI::InvalidURIError
      false
    end

    def notify_user
      notify = user.preferences[:remotestorage_notify_auth_created]

      case notify
      when "xmpp"
        router = Router.new
        payload = {
          type: "normal", to: user.address,
          from: Setting.xmpp_notifications_from_address,
          body: "You have just granted '#{self.client_id}' access to your Kosmos Storage. Visit your Storage dashboard to check on your connected apps and revoke permissions anytime: #{router.services_storage_url}"
        }
        XmppSendMessageJob.perform_later(payload)
      when "email"
        NotificationMailer.with(user: user, auth: self)
                          .remotestorage_auth_created.deliver_later
      end
    end
end
