class Oauth::Application < ApplicationRecord
  include ::Doorkeeper::Models::Ownership
  include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
  include ::Doorkeeper::Models::Ownership

  has_many :access_tokens, foreign_key: :application_id, dependent: :delete_all,
    class_name: 'Oauth::AccessToken', inverse_of: :application

  has_many :authorized_tokens, -> { where(revoked_at: nil) }, foreign_key: :application_id,
    class_name: 'Oauth::AccessToken', inverse_of: :application

  has_many :access_grants, foreign_key: :application_id, dependent: :delete_all,
    class_name: 'Oauth::AccessToken', inverse_of: :application

  alias_attribute :client_id, :uid
  alias_attribute :active, :confidential

  before_validation :set_scope, if: :scopes_changed?

  validate :valid_token

  before_save :revoke_access_tokens, unless: :active?

  SCOPES = {
    roster: [
      'https://purl.imsglobal.org/spec/or/v1p1/scope/roster-core.readonly',
      'https://purl.imsglobal.org/spec/or/v1p1/scope/roster.readonly'
    ],
    gradebook: ['https://purl.imsglobal.org/spec/or/v1p1/scope/gradebook.readonly']
  }

  def scope_keys
    scopes.map do |scope|
      SCOPES.key(SCOPES.values.find { |s| s.include?(scope) })
    end.uniq
  end

  private
    def set_scope
      self.scopes = scopes.map { |s| SCOPES[s.to_sym] }.join(' ')
    end

    def revoke_access_tokens
      authorized_tokens.each { |t| t.update(revoked_at: Time.zone.now) }
    end

    def valid_token
      return self.class_name_token = nil if class_name_token.blank?

      valid_tokens = ['<class_name>', '<period_name>', '<school_year>']
      tokens = class_name_token.scan(/<(..*?)>/).flatten
      return errors.add(:class_name_token, 'token must exist') if tokens.empty?
      return errors.add(:class_name_token, 'start with <') unless class_name_token.first == '<'
      return errors.add(:class_name_token, 'end with >') unless class_name_token.last == '>'

      tokens.each do |token|
        next if valid_tokens.include?("<#{token}>")

        return errors.add(:class_name_token, 'invalid token')
      end

      regex = Regexp.new(valid_tokens.push('\s').join('|'))
      stripped_token = class_name_token.gsub(regex, '')
      return if stripped_token.blank? || stripped_token.scan(/^-+$/).present?

      errors.add(:class_name_token, 'invalid characters')
    end
end
