class User < ActiveRecord::Base
  OMNIAUTH_EMAIL_PREFIX = 'omniauth@participacion'
  OMNIAUTH_EMAIL_REGEX  = /\A#{OMNIAUTH_EMAIL_PREFIX}/

  include Verification

  apply_simple_captcha
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :async

  acts_as_voter
  acts_as_paranoid column: :hidden_at
  include ActsAsParanoidAliases

  has_one :address
  has_one :administrator
  has_one :moderator
  has_one :organization
  has_one :lock
  has_many :flags
  has_many :identities, dependent: :destroy
  has_many :debates, -> { with_hidden }, foreign_key: :author_id
  has_many :comments, -> { with_hidden }

  validates :username, presence: true, unless: :organization?
  validates :username, uniqueness: true, unless: :organization?
  validate :validate_username_length

  validates :official_level, inclusion: {in: 0..5}
  validates_format_of :email, without: OMNIAUTH_EMAIL_REGEX, on: :update
  validates :terms_of_service, acceptance: { allow_nil: false }, on: :create

  validates_associated :organization, message: false

  accepts_nested_attributes_for :organization

  scope :administrators, -> { joins(:administrators) }
  scope :moderators,     -> { joins(:moderator) }
  scope :organizations,  -> { joins(:organization) }
  scope :officials,      -> { where("official_level > 0") }
  scope :for_render,     -> { includes(:organization) }

  def self.find_for_oauth(auth, signed_in_resource = nil)
    # Get the identity and user if they exist
    identity = Identity.find_for_oauth(auth)

    # If a signed_in_resource is provided it always overrides the existing user
    # to prevent the identity being locked with accidentally created accounts.
    # Note that this may leave zombie accounts (with no associated identity) which
    # can be cleaned up at a later date.
    user = signed_in_resource ? signed_in_resource : identity.user
    user ||= first_or_create_for_oauth(auth)

    # Associate the identity with the user if needed
    identity.update_user(user)
    user
  end

  # Get the existing user by email if the provider gives us a verified email.
  # If no verified email was provided we assign a temporary email and ask the
  # user to verify it on the next step via RegistrationsController.finish_signup
  def self.first_or_create_for_oauth(auth)
    email = auth.info.email if auth.info.verified || auth.info.verified_email
    user  = User.where(email: email).first if email

    # Create the user if it's a new registration
    if user.nil?
      user = User.new(
        username: auth.info.nickname || auth.extra.raw_info.name.parameterize('-') || auth.uid,
        email: email ? email : "#{OMNIAUTH_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
        password: Devise.friendly_token[0,20]
      )
      user.skip_confirmation!
      user.save!
    end

    user
  end

  def name
    organization? ? organization.name : username
  end

  def debate_votes(debates)
    voted = votes.for_debates(debates)
    voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
  end

  def comment_flags(comments)
    comment_flags = flags.for_comments(comments)
    comment_flags.each_with_object({}){ |f, h| h[f.flaggable_id] = true }
  end

  def administrator?
    administrator.present?
  end

  def moderator?
    moderator.present?
  end

  def organization?
    organization.present?
  end

  def verified_organization?
    organization && organization.verified?
  end

  def official?
    official_level && official_level > 0
  end

  def add_official_position!(position, level)
    return if position.blank? || level.blank?
    update official_position: position, official_level: level.to_i
  end

  def remove_official_position!
    update official_position: nil, official_level: 0
  end

  def block
    debates_ids = Debate.where(author_id: id).pluck(:id)
    comments_ids = Comment.where(user_id: id).pluck(:id)

    self.hide
    Debate.hide_all debates_ids
    Comment.hide_all comments_ids
  end


  def email_provided?
    !!(email && email !~ OMNIAUTH_EMAIL_REGEX) ||
      !!(unconfirmed_email && unconfirmed_email !~ OMNIAUTH_EMAIL_REGEX)
  end

  def locked?
    Lock.find_or_create_by(user: self).locked?
  end

  def self.search(term)
    term.present? ? where("email = ? OR username ILIKE ?", term, "%#{term}%") : none
  end

  def self.username_max_length
    @@username_max_length ||= self.columns.find { |c| c.name == 'username' }.limit
  end

  def show_welcome_screen?
    sign_in_count == 1 && unverified? && !organization
  end

  private

    def validate_username_length
      validator = ActiveModel::Validations::LengthValidator.new(
        attributes: :username,
        maximum: User.username_max_length)
      validator.validate(self)
    end

end
