 RUBYCRITIC
RUBYCRITIC
      
    require 'textacular/searchable'
class Invalidation < ActiveRecord::Base  -                 Invalidation has no descriptive comment            
-                 Invalidation has at least 34 methods            
extend Searchable(:id, :summary, :details, :petition_id)
  include Browseable
  belongs_to :petition
  has_many :signatures
  facet :all,       -> { by_most_recent }
  facet :completed, -> { completed.by_most_recent }
  facet :cancelled, -> { cancelled.by_most_recent }
  facet :pending,   -> { pending.by_most_recent }
  facet :enqueued,  -> { enqueued.by_most_recent }
  facet :running,   -> { running.by_longest_running }
  CONDITIONS = %i[
    petition_id name postcode ip_address
    email domain constituency_id location_code
    created_before created_after
  ]
  validates :summary, presence: true, length: { maximum: 255 }
  validates :details, length: { maximum: 10000 }
  validates :petition_id, numericality: { only_integer: true, allow_blank: true, greater_than_or_equal_to: 100000 }
  validates :name, length: { maximum: 255, allow_blank: true }
  validates :postcode, length: { maximum: 255, allow_blank: true }
  validates :ip_address, length: { maximum: 20 }, format: { with: /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/ }, allow_blank: true
  validates :email, length: { maximum: 255, allow_blank: true }
  validates :domain, length: { maximum: 255, allow_blank: true }
  validates :constituency_id, length: { maximum: 30, allow_blank: true }
  validates :location_code, length: { maximum: 30, allow_blank: true }
  validate do
    if applied_conditions.empty?
      errors.add :petition_id, "Please select some conditions, otherwise all signatures will be invalidated"
    end
    if petition_id?
      errors.add :petition_id, "Petition doesn't exist" unless Petition.exists?(petition_id)
    end
    if domain?
      errors.add :domain, "Please enter a domain and not an email address" if domain =~ /@/
    end
    if constituency_id?
      errors.add :constituency_id, "Constituency doesn't exist" unless Constituency.exists?(external_id: constituency_id)
    end
    if location_code?
      errors.add :location_code, "Location doesn't exist" unless Location.exists?(code: location_code)
    end
    if created_before? && created_after?
      errors.add :created_after, "Starting date is after the finishing date" unless created_before > created_after
    end
  end
  before_destroy do
    !started?
  end
  class << self
    def by_most_recent
      reorder(created_at: :desc)
    end
    def by_longest_running
      reorder(started_at: :asc)
    end
    def cancelled
      where(arel_table[:cancelled_at].not_eq(nil))
    end
    def completed
      where(arel_table[:completed_at].not_eq(nil))
    end
    def enqueued
      where(arel_table[:enqueued_at].not_eq(nil).and(arel_table[:started_at].eq(nil)))
    end
    def not_completed
      where(arel_table[:completed_at].eq(nil))
    end
    def pending
      where(enqueued_at: nil, started_at: nil, cancelled_at: nil, completed_at: nil)
    end
    def running
      started.not_completed
    end
    def started
      where(arel_table[:started_at].not_eq(nil))
    end
  end
  def cancel!(now = Time.current)  -                 Invalidation has missing safe method 'cancel!'            
return false if cancelled? || completed?
    update(cancelled_at: now)
  end
  def cancelled?
    cancelled_at?
  end
  def completed?
    completed_at?
  end
  def count!  -                 Invalidation has missing safe method 'count!'            
return false unless pending?
    update(matching_count: matching_signatures.count, counted_at: Time.current)
  end
  def start!  -                 Invalidation has missing safe method 'start!'            
return false unless pending?
    InvalidateSignaturesJob.perform_later(self)
    update(enqueued_at: Time.current)
  end
  def started?
    started_at?
  end
  def enqueued?
    enqueued_at?
  end
  def pending?
    !(enqueued? || started? || cancelled? || completed?)
  end
  def running?
    started? && !(completed? || cancelled?)
  end
  def percent_completed
    if started? || completed?
      matching_count.zero? ? 100 : calculate_percent_complete
    else
      matching_count.zero? ? 0 : calculate_percent_complete
    end
  end
  def matching_signatures  -                 Invalidation#matching_signatures has a flog score of 27            
-                 Invalidation#matching_signatures has approx 11 statements            
scope = Signature.for_invalidating
    scope = petition_scope(scope) if petition_id?
    scope = name_scope(scope) if name?
    scope = postcode_scope(scope) if postcode?
    scope = ip_address_scope(scope) if ip_address?
    scope = email_scope(scope) if email?
    scope = domain_scope(scope) if domain?
    scope = constituency_id_scope(scope) if constituency_id?
    scope = location_code_scope(scope) if location_code?
    scope = date_range_scope(scope) if date_range?
    scope
  end
  def invalidate!  -                 Invalidation#invalidate! has a flog score of 26            
-                 Invalidation has missing safe method 'invalidate!'            
-                 Invalidation#invalidate! has approx 9 statements            
return if cancelled? || completed?
    update(
      started_at: Time.current,  -       
matching_count: matching_signatures.count,
      counted_at: Time.current  -       
)
    Appsignal.without_instrumentation do
      matching_signatures.find_in_batches(batch_size: 100) do |signatures|
        signatures.each do |signature|  -                 Invalidation#invalidate! contains iterators nested 2 deep            
signature.invalidate!(Time.current, self)  -       
increment!(:invalidated_count)
        end
        reload and return if cancelled?
      end
    end
    update(completed_at: Time.current)  -       
end
  private
  def petition_scope(scope)
    scope.where(petition_id: petition_id)
  end
  def name_scope(scope)
    if name =~ /%/
      scope.where("lower(name) LIKE ?", name.strip.downcase)  -       
-       
else
      scope.where("lower(name) = ?", name.strip.downcase)  -       
-       
end
  end
  def postcode_scope(scope)
    scope.where(postcode: postcode)
  end
  def ip_address_scope(scope)
    scope.where(ip_address: ip_address)
  end
  def email_scope(scope)
    if email =~ /%/
      scope.where("email LIKE ?", email)
    else
      scope.where("email = ?", email)
    end
  end
  def domain_scope(scope)
    if domain =~ /%/
      scope.where("SUBSTRING(email FROM POSITION('@' IN email) + 1) LIKE ?", domain)
    else
      scope.where("SUBSTRING(email FROM POSITION('@' IN email) + 1) = ?", domain)
    end
  end
  def constituency_id_scope(scope)
    scope.where(constituency_id: constituency_id)
  end
  def location_code_scope(scope)
    scope.where(location_code: location_code)
  end
  def date_range?
    created_before? || created_after?
  end
  def date_range_scope(scope)
    if created_before?
      scope = scope.where(table[:created_at].lt(created_before))  -       
end
    if created_after?
      scope = scope.where(table[:created_at].gt(created_after))  -       
end
    scope
  end
  def table  -                 Invalidation#table doesn't depend on instance state (maybe move it to another class?)            
Signature.arel_table
  end
  def calculate_percent_complete
    [[0, ((invalidated_count.to_f / matching_count.to_f) * 100).floor].max, 100].min
  end
  def applied_conditions
    CONDITIONS.select{ |c| read_attribute(c).present? }  -                 Invalidation#applied_conditions has the variable name 'c'            
end
end