class FieldsDefinition < ActiveRecord::Base

  DEFAULT_LABELS = {
    "org_name" => "Organization/Group Name",
    "org_legaladdress" => "Legal Address"
  }.freeze


  @targets = []
  class << self
    attr_reader :targets
  end

  def self.push_target(klass)
    @targets << klass.to_s
  end

  serialize :choices, Array

  belongs_to :account, :inverse_of => :fields_definitions
  acts_as_list :scope => :account, :column => :pos

  scope :by_provider,  lambda {|provider| where(['account_id = ?', provider.id]) }
  scope :by_target, lambda { |class_name| where(['target = ?', class_name])}
  scope :by_name, lambda { |name| where(['name = ?', name])}
  scope :required, -> { where({ :required => true })}

  def self.editable_by(user)
    select{ |fd| fd.editable_by?(user) }
  end

  before_validation :set_required_if_required_field_on_target

  validates :label, :target, :name, presence: true, length: { maximum: 255 }
  validates :name, format: { :with =>/\A[A-Za-z][A-Za-z\d_-]*\z/, :message => "Name should start with letters, and can contain numbers, - and _" }
  validates :name, uniqueness: { :scope => [:account_id, :target] }
  validates :name, :exclusion => Fields::ExtraField::InputField.excludes
  validates :choices, :hint, length: { maximum: 65535 }

  validate :allowed_fields_definition_name, :check_base_validations, :only_choices_for_text, :read_only_billing_address

  before_create :set_last_position_in_target_scope

  before_destroy :avoid_destroy_required_field_on_target

  default_scope { by_position }
  scope :by_position, -> { order(:pos) }

  attr_protected :account_id, :tenant_id
  attr_readonly :account_id, :tenant_id, :target, :name

  alias_attribute :position, :pos

  # this is tested in unit/account_test
  def self.create_defaults(account)
    targets.each do |target|
      klass = target.constantize
      klass.required_fields.each do |f|
        label = DEFAULT_LABELS[f] || f.humanize
        account.fields_definitions.create!({ :target => target, :name => f,
                                             :label => label, :required => true })
      end
    end
  end

  def editable_by?(user)
    acc = user.try!(:account)
    # if self.account == acc
    if acc && !acc.buyer?
      true
    else
      not read_only? and not hidden?
    end
  end

  def targets
    self.class.targets
  end

  def target=(t)
    self[:target] = t if targets.include?(t)
  end

  def visible_for?(user)
    if self.account != user.account
      !self.hidden?
    else
      true
    end
  end

  def target_class
    return if target.nil?

    target.constantize
  end

  def required_field_on_target?
    target_class.required_fields.include?(name)
  end

  def choices_for_views=(values)
    splitted_values = values.split(/\s*,\s*/) if values
    self[:choices] = splitted_values
  end

  def choices_for_views
    choices_values = self[:choices].join(", ") if self[:choices]
    choices_values
  end

  private

  def set_last_position_in_target_scope
    fields_for_target = self.account.fields_definitions.where(target: target)
    max_position = fields_for_target.maximum(:pos) || 0
    self.position = max_position + 1
  end

  def allowed_fields_definition_name
    return if target.nil? # the target presence validation already caught this

    forbidden = ( (target_class.new.attributes.keys| target_class.column_names) -
                  target_class.builtin_fields)
    if forbidden.include?(name)
      errors.add(:name, "Field name is not allowed")
    end
  end

  def check_base_validations
    return if target.nil? # the target presence validation already caught this

    if self.required? && (self.hidden? || self.read_only?)
      errors.add(:required,  "Fields cannot be required AND hidden/read_only")
    end
  end

  def only_choices_for_text
    return if target.nil? # the target presence validation already caught this

    if !choices.blank?
      if target_class.builtin_fields.include?(name) && # It's allowed by 3scale and in DB
         !target_class.columns_hash[name].try!(:text?)  # It's an attribute. relations can't have custom choices
        errors.add(:choices,  "Only text fields can have choices")
      end
    end
  end

  def set_required_if_required_field_on_target
    return if target.nil?

    if required_field_on_target?
      self.required  = true
      self.hidden    = false
      self.read_only = false
    end

    true
  end

  def read_only_billing_address
    if target_class == Account && name == 'billing_address' && !self.read_only?
      errors.add(:read_only, "billing_address has to be read_only")
    end
  end

  def avoid_destroy_required_field_on_target
    !required_field_on_target?
  end

end
