class Expedient < ApplicationRecord
  audited
  include PgSearch

  acts_as_taggable_on :tags

  has_many :assets, as: :assetable, dependent: :destroy
  has_many :events, as: :eventable, dependent: :destroy
  has_many :managements, dependent: :destroy
  accepts_nested_attributes_for :managements, reject_if: :not_a_quick_case?
  belongs_to :expedient, optional: true
  belongs_to :institution
  belongs_to :branch
  belongs_to :entry_way
  belongs_to :city, optional: true
  belongs_to :department, class_name: 'State', foreign_key: 'state_id', optional: true
  belongs_to :user

  validates :institution_id, :entry_way_id, :received_at, :comment, presence: true

  after_create :notify_admins
  before_create :set_reference_code
  delegate :new?, :closed?, :process?, :redirected?, to: :current_state

  scope :for_assigned,      -> (user_id) { joins(:managements).where('managements.assigned_ids @> ?', "{#{user_id}}").pluck('expedients.id') }
  scope :status,            -> (status) { where(state: status) }
  scope :by_aa,             -> (area) { joins(:managements).where('managements.assigned_ids && ARRAY[?]::integer[]', User.by_administrative_area(area).ids) }
  scope :by_anon,           -> (anon=1) { where(anonymous: anon) }
  # scope :by_ct_failed,      -> { by_anon(0).where("expedients.contact_time IS NULL").joins(:events).where(events: {state: 'failed_communication_attempt'})}
  scope :by_ct_failed,      -> { by_anon(0).where("expedients.contact_business_days IS NULL").joins(:events).where(events: {state: 'failed_communication_attempt'})}
  # scope :by_ct_nil,         -> { by_anon(0).where(contact_time: nil).where.not(id: by_ct_failed.distinct.ids) }
  scope :by_ct_nil,         -> { by_anon(0).where(contact_business_days: nil).where.not(id: by_ct_failed.distinct.ids) }
  # scope :by_ct_less,        -> { by_anon(0).where("contact_time <= 2880") }
  scope :by_ct_less,        -> { by_anon(0).where("contact_business_days <= 2") }
  scope :by_ct_more,        -> { by_anon(0).where("contact_business_days > 2") }
  scope :by_tags,           -> (tag) { tagged_with("#{tag}", :any => true) }
  scope :tag_counts_custom, -> (expedients_ids) { ActsAsTaggableOn::Tag.joins(:taggings).select('tags.id, tags.name, tags.taggings_count, COUNT(taggings.id) as count')
                              .group('tags.id, tags.name, tags.taggings_count').where(taggings: { taggable_type: 'Expedient', taggable_id: expedients_ids} )}
  scope :by_institution,    -> (institution_id) { where(institution_id: institution_id) }

  enum classification_type: [:delivered,
                             :without_competence,
                             :duplicity,
                             :comment,
                             :lack_information,
                             :citizen_withdrawal,
                             :administrative_info,
                             :without_info]

  CLASSIFICATION_TYPES_4_NEW = Expedient.classification_types.except(:delivered,
                             :without_competence,
                             :comment,
                             :lack_information,
                             :citizen_withdrawal,
                             :administrative_info)

  WITHOUT_MANAGEMENTS = %w(duplicity without_info)

  attr_accessor :justification, :quick_case
  # TODO Validations with quick case
  # validates :justification

  #DEFAULT_SEARCH_PARAMETER = {utf8: "✓", q: {created_at_gteq: , created__at_lteq: }

  def set_correlative_and_code
    self.correlative_number = institution.correlative
    self.correlative    = "#{institution.acronym}-#{confirmed_at.year}-#{institution.correlative.to_s.rjust(3, '0')}"
    if self.save
      institution.update_column(:correlative, correlative_number + 1)
    else
      self.errors.full_messages
    end
  end

  def not_a_quick_case?
    self.quick_case.to_i == 0
  end

  def kind
    entry_way.try(:name)
  end

  STATE = {
    'new'        => 'Nuevo',
    'process'    => 'En proceso',
    'redirected' => 'Redireccionado',
    'closed'     => 'Cerrado'
  }

  GENDER = {
    'm' => 'Hombre',
    'f' => 'Mujer'
  }

  RESOLUTION_TYPE = {
    'positive' => 'Positiva',
    'negative' => 'Negativa',
    'partially' => 'Parcialmente',
  }

  def resolution_type_s
    resolution_type.blank? ? 'N/A' : RESOLUTION_TYPE[resolution_type]
  end

  def it_proceeds_s
    it_proceeds == 0 ? 'No' : (it_proceeds == 1 ? 'Si' : 'N/A')
  end

  def state_s
    STATE[state]
  end

  def gender_s
    GENDER[gender] || 'N/A'
  end

  def current_state
    state.inquiry
  end

  def hours_passed
    ((Time.current - created_at) / 1.hour).round
  end

  def days_passed
    ((Time.current - created_at) / 1.day).round
  end

  def institution_name
    institution.try(:name)
  end

  def branch_name
    branch.try(:name)
  end

  def set_reference_code
    self.reference_code = generate_reference_code.upcase
  end

  def generate_reference_code(length = 6)
    generated_code = (0..length).map{ rand(36).to_s(36) }.join
    return generated_code unless self.class.exists?(reference_code: generated_code)

    generate_reference_code
  end

  def is_closed?
    closed? || redirected?
  end

  def code
    correlative || reference_code
  end

  def response_time
    (events.where(state: 'closed').first.try(:created_at) - events.where(state: 'process').first.try(:created_at)) / 1.hour rescue 0
  end

  ransacker :assigned_ids do
    Arel.sql("array_to_string(managements.assigned_ids, ',')")
  end

  def self.ransackable_scopes(auth_object = nil)
    [:by_aa, :by_ct_nil, :by_ct_more, :by_ct_less, :by_anon, :by_ct_failed, :by_tags]
  end

  def tags_string
    tags.pluck(:name).join(', ')
  end

  def all_assigned_names
    all_ids = managements.map{|m| m.assigned_ids.map{|id| id.nil? ? m.user.id : id} }.flatten
    @all_assigned_names ||= User.where(id: all_ids).pluck(:name, :administrative_area).map{|o| o[1].blank? ? o[0] : "#{o[0]} (#{o[1]})"}.join(', ')
  end

  def managements_count
    c = managements.ids.length
    c > 0 ? c : 1
  end

  def contacted
    # contact_time.nil? ? "No" : "Si" TODO delete later
    contact_business_days.nil? ? "No" : "Si"
  end

  def contact_time_string
    r = ''
    if anonymous
      r = 'Anónimo'
    else
      if !contact_business_days.nil?
        if contact_business_days <= 2
          r = "Menos de 48 horas"
        else
          r = "Más de 48 horas"
        end
      else
        if events.where(events: {state: 'failed_communication_attempt'}).any?
          r = "Contacto Fallido"
        else
          r = "Sin contacto"
        end
      end
    end
    r
  end

  def contact_at
    events.where(state: 'process').first.try(:start_at)
  end

  def closed_at
    events.where(state: 'closed').first.try(:start_at)
  end

  def redirected_to
    return 'N/A' if !redirected?
    Expedient.where(expedient_id: self.id).first.try(:institution_name)
  end

  def closed_info
    events.where(state: 'closed').first.try(:justification)
  end

  def closed_classification
    closed? && classification_type.present? ? I18n.t(classification_type, scope: 'activerecord.enum.expedient.classification_type') : 'N/A'
  end

  def notify_admins
    ApplicationMailer.notify_new_expedient(self).deliver_now unless Rails.env.development?
  end

  def management_types
    managements.pluck(:kind).uniq
  end

  def needs_resolution_type?
    (management_types & ['claim', 'request', 'query']).any?
  end

  def needs_it_proceeds?
    (management_types & ['complaint', 'notice', 'suggestion']).any?
  end
end
