class User < ApplicationRecord
  include Contextuable
  include UserImportable

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable, :recoverable, :timeoutable
  devise :encryptable if Settings.devise_encryptor && Settings.devise_encryptor != "bcrypt"

  alias_attribute :password_salt, :salt
  alias_attribute :encrypted_password, :crypted_password

  # NOTE: this must place before dependent: :destroy to ensure the callback will be called first
  before_destroy :check_user_statistics

  belongs_to :title, optional: true
  belongs_to :country, class_name: IptcCountry.name, optional: true

  has_one :title_provider_group_name, through: :title
  has_one :user_setting, dependent: :destroy
  has_one :billing_company, :through => :title
  has_one :billing_information # for now we sync only one billing information
  has_many :billing_informations # we use this for making the form accept multiple billing informations
  has_many :light_boxes, dependent: :destroy
  has_many :title_light_boxes, :through => :title, :source => :light_boxes
  has_many :statistics
  has_many :photos, dependent: :destroy
  has_many :title_provider_groups, through: :title_provider_group_name
  has_many :providers, through: :title_provider_groups
  has_many :images, through: :providers
  has_many :title_light_box_images, through: :title_light_boxes, :source => :images
  has_many :billings

  has_one :photographer_billing_setting, dependent: :destroy
  has_many :photographer_billing_companies, dependent: :destroy
  has_many :photographer_billings, dependent: :destroy
  has_many :photographer_payments, dependent: :destroy

  scope :admin, -> { where(roles_mask: 1) }
  scope :active, -> { where(status: :active) }

  attr_accessor :comment

  validate :title_provider_group_name_id, if: -> { active? && !context_import? }
  validates :civility, :first_name, :last_name, :activity_sector, :company,
            :address, :zip_code, :city, :phone, presence: true, if: -> { context_client? }

  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP, allow_blank: true }
  validates :email, uniqueness: { case_sensitive: false }, if: -> { !context_register? && email_changed? && automatic_title_creation? }

  validates_format_of :first_name, with: /[a-zA-Z]/, allow_blank: true
  validates_format_of :last_name, with: /[a-zA-Z]/, allow_blank: true
  validates_format_of :phone, with: /\A[\\+\\\-\\\s\\(\\)\\: 0-9]*\z/i
  validates_format_of :company, with: /[a-zA-Z]/, allow_blank: true
  validates_format_of :address, with: /[a-zA-Z]/, allow_blank: true
  validates_format_of :city, with: /[a-zA-Z]/, allow_blank: true
  validates_format_of :activity_sector, with: /[a-zA-Z]/, allow_blank: true

  validate :exist_email_on_register, if: -> { context_register? && automatic_title_creation? }
  validate :exist_email_on_title, if: -> { (email_changed? || title_id_changed?) && !automatic_title_creation? }
  validates :login, uniqueness: { case_sensitive: false, allow_blank: true }
  validates :terms_of_service, acceptance: true
  validate :password_complexity, if: -> {crypted_password_changed?}
  validates :password, confirmation: true, if: -> {crypted_password_changed? || password_confirmation.present?}

  validates_presence_of :password, :password_confirmation, on: :create, unless: -> { context_import? }

  before_create :initialize_user_data
  after_create :create_user_setting

  accepts_nested_attributes_for :title, :billing_informations

  ransacker :full_name do
    Arel.sql("concat(#{table_name}.first_name, ' ', #{table_name}.last_name)")
  end

  def self.statistics_users_count
    @statistic_user_ids ||= Statistic.distinct.pluck(:user_id)
    (pluck(:id) & @statistic_user_ids).size
  end

  def full_name(with_civility = false)
    if with_civility
      [I18n.t("civilities.#{civility}"), first_name, last_name].compact.join(' ')
    else
      [first_name, last_name].compact.join(' ')
    end
  end

  def full_code
    ActiveSupport::Inflector.transliterate([first_name, last_name, id].compact.join('_').strip.downcase)
  end

  def status_text
    I18n.t("user_statuses.#{status}")
  end

  def password_complexity
    min = admin? ? 16 : 8
    max = 25
    return if password.blank? || password =~ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[-.\#$+!*@%&\/?])[-.\#$+!*@%&\/?a-zA-Z\d]{#{min},#{max}}$/

    errors.add :password, I18n.t('password_complexity_error', min: min, max: max)
  end

  def active?
    status&.to_sym == :active
  end

  def roles_mask_text
    @roles_mask_text ||= I18n.t('user_roles').values
    @roles_mask_text[Math.log(roles_mask, 2).to_i] rescue nil
  end

  def roles_mask_symbol
    @roles_mask_symbol ||= I18n.t('user_roles').keys
    @roles_mask_symbol[Math.log(roles_mask, 2).to_i] rescue nil
  end

  def superadmin?
    roles_mask == 1
  end

  def provider_admin?
    roles_mask == 8
  end

  def provider_user?
    roles_mask == 16
  end

  def admin?
    roles_mask.in?([1, 2, 8, 128])
  end

  def sign_up_initialize
    self.status = 'pending'
  end

  def after_sign_up
    assign_attributes(context: nil)
    set_login
    set_title_name if automatic_title_creation?
    send_user_update_job
  end

  def destroy_automatic_title_creation
    if automatic_title_creation?
      if title && title.name&.match(/_#{id}$/) && title.users.blank?
        title if title.destroy
      end
    end
  end

  def send_user_update_job
    Legacy::UserUpdateJob.perform_later(self, saved_changes) if Parameter.load('sync_data_to_pixadmin')
  end

  protected

  # override Devise::Recoverable
  def send_reset_password_instructions_notification(token)
    UserMailer.with(user: self).reset_password_instructions(token).deliver_now
  end

  private

  def initialize_user_data
    self.persistence_token ||= SecureRandom.hex(64)
    self.perishable_token ||= SecureRandom.urlsafe_base64(15)
  end

  def set_login
    self.login = "#{normalized(Server.find_by(is_self: true)&.name)}_#{normalized(first_name)}_#{normalized(last_name)}_#{id}"
    save!
  end

  def set_title_name
    if title
      title.update!(name: "#{first_name}#{last_name}_#{id}") if title.name.blank?
    else
      create_title!(name: "#{first_name}#{last_name}_#{id}")
      save!
    end
  end

  def check_user_statistics
    throw :abort if statistics.exists?
  end

  def exist_email_on_register
    if User.exists?(email: email)
      errors.add(:base, I18n.t("email_register_existed_error"))
    end
  end

  def exist_email_on_title
    if User.where("title_id = ? AND email = ?", title_id, email).exists?
      errors.add(:base, I18n.t("email_existed_error", title: title.name, email: email))
    end
  end

  def title_provider_group_name_id
    errors[:'title.title_provider_group_name_id'] << I18n.t('errors.messages.required') if title&.title_provider_group_name_id.blank?
  end

  def automatic_title_creation?
    Parameter.load('automatic_title_creation')
  end
end
