module TwoFactorAuthentication
  class Device < ActiveRecord::Base
    default_scope { order('id ASC') }

    belongs_to :user
    validates_presence_of :user_id
    validates_presence_of :identifier

    # Check uniqueness of default for this type
    validate :cannot_set_default_if_exists

    def self.get_default
      find_by(default: true, active: true)
    end

    def self.get_active
      where(active: true)
    end

    def self.has_default?(user)
      Device.where(user_id: user.id, active: true, default: true).exists?
    end

    def has_default?
      self.class.has_default? user
    end

    ##
    # Make the device active, and set it as default if no other device exists
    def confirm_registration_and_save
      self.active = true
      self.default = !has_default?

      save
    end

    def identifier
      value = read_attribute(:identifier)

      if value
        value
      else
        default_identifier
      end
    end

    def redacted_identifier
      identifier
    end

    def default_identifier
      if user.present?
        "#{name} (#{user.login})"
      else
        name
      end
    end

    def name
      model_name.human
    end

    def active?
      active == true
    end

    def make_default!
      return false unless active?

      Device.transaction do
        Device.where(user_id: user_id).update_all(default: false)
        self.update_column(:default, true)
        return true
      end
    end

    def channel=(value)
      super value.to_sym
    end

    def channel
      value = read_attribute(:channel)
      return if value.nil?

      value.to_sym
    end

    def self.device_type
      raise NotImplementedError
    end

    def self.available_channels_in_strategy
      strategy_class = manager.get_strategy(device_type)
      strategy_class.supported_channels & self.supported_channels
    end

    private

    def self.manager
      ::OpenProject::TwoFactorAuthentication::TokenStrategyManager
    end

    def cannot_set_default_if_exists
      if default && has_default?
        errors.add :default, :default_already_exists
      end

      true
    end
  end
end
