class Comment < ActiveRecord::Base
  # Accessors
  attr_accessible :message, :comment_id, :attachments_attributes, :commentable_id, :commentable_type
  attr_accessible :is_requirement, :collected, :justification
  attr_accessible :information_request_requirements_attributes
  attr_accessor :assigned_users, :invited_users

  # Constants
  MENTION_REGEX = /@\[(.*?)\]/
  STATES = %w[new process closed]

  # Associations
  belongs_to :user
  belongs_to :commentable, polymorphic: true
  belongs_to :comment
  has_many :comments, dependent: :destroy
  has_many :attachments, as: :attachable, dependent: :destroy
  accepts_nested_attributes_for :attachments, reject_if: :all_blank, allow_destroy: true
  has_many :events, as: :eventable, class_name: "InformationRequestEvent", dependent: :destroy
  has_many :comment_users, dependent: :destroy
  has_many :users, through: :comment_users
  has_many :information_request_requirements, dependent: :destroy
  accepts_nested_attributes_for :information_request_requirements, reject_if: :all_blank, allow_destroy: true

  # Scopes
  scope :roots, where(comment_id: nil)
  scope :requirements, where(is_requirement: true)
  default_scope order("comments.created_at ASC")

  # Validations
  validates :message, :commentable_id, :commentable_type, presence: true
  validates :justification, presence: true, if: Proc.new{|o| o.collected?}

  # Delegates
  delegate :name, :avatar, :email, :role_name, to: :user, allow_nil: true, prefix: true
  delegate :process?, :closed?, to: :current_state

  # Callbacks
  after_create :process_assigned_users, if: Proc.new{|o| o.assigned_users.present?}
  after_create :process_invited_users, if: Proc.new{|o| o.invited_users.present?}

  def name
    ""
  end

  # Methods
  def root?
    comment_id.nil?
  end

  # obtain current_state of comment
  def current_state
    #(events.last.try(:state) || STATES.first).inquiry
    state.inquiry
  end

  # obtain last event business days, to current date
  def last_business_days
    events.order("id DESC").limit(1).first.try(:business_days_passed)
  end

  # get a string of state
  def get_state
    I18n.t("label.#{state}")
  end

  # invite users to thread, if params comment_users is present
  def process_assigned_users
    if commentable.is_a?(InformationRequest)
      unless assigned_users.blank?
        # obtain assigned users object
        all_assigned_users = User.where(email: assigned_users)
        # assign users to comment
        all_assigned_users.each{|user| comment_users.create(user_id: user.id)}
        # assign permissions
        request_permissions = Permission.where(subject_class: 'InformationRequest', action: ['read','update'])
        comment_permissions = Permission.where(subject_class: 'Comment', action: ['read','create'])
        information_request = commentable
        all_assigned_users.each do |assigned_user|
          unless assigned_user.can?(:manage, :all) or assigned_user.can?(:process_request, InformationRequest)
            # add permissions to request
            request_permissions.each do |permission|
              permissions_role = PermissionsRole.where(permission_id: permission.id, role_id: assigned_user.role_id).first_or_initialize
              if permissions_role.new_record?
                permissions_role.conditions = { institution_id: Array.wrap(information_request.institution_id.to_s), id: Array.wrap(commentable_id.to_s) }
              else
                hash_conditions = permissions_role.symboled_conditions
                # Add institution_id to institution ids permission
                if hash_conditions["institution_id"]
                  hash_conditions["institution_id"] << information_request.institution_id unless Array.wrap(hash_conditions["institution_id"]).include?(information_request.institution_id)
                else
                  hash_conditions["institution_id"] = Array.wrap(information_request.institution_id)
                end
                # Add specific id to ids conditions
                if hash_conditions["id"]
                  hash_conditions["id"] << commentable_id unless hash_conditions["id"].include?(commentable_id)
                else
                  hash_conditions["id"] = Array.wrap(commentable_id)
                end
                # parse conditions ids to string
                hash_conditions["institution_id"] = Array.wrap(hash_conditions["institution_id"]).map(&:to_s)
                hash_conditions["id"] = hash_conditions["id"].map(&:to_s)
                # assign new hash conditions
                permissions_role.conditions = hash_conditions
              end
              permissions_role.save
            end
            # add permissions to comment
            comment_permissions.each do |permission|
              permissions_role = PermissionsRole.where(permission_id: permission.id, role_id: assigned_user.role_id).first_or_initialize
              if permissions_role.new_record?
                permissions_role.conditions = { id: Array.wrap(self.id.to_s) }
              else
                hash_conditions = permissions_role.symboled_conditions
                # Add comment id to ids permission
                if hash_conditions["id"]
                  hash_conditions["id"] << self.id unless Array.wrap(hash_conditions["id"]).include?(self.id)
                else
                  hash_conditions["id"] = Array.wrap(self.id)
                end
                # parse conditions ids to string
                hash_conditions["id"] = Array.wrap(hash_conditions["id"]).map(&:to_s)
                # assign new hash conditions
                permissions_role.conditions = hash_conditions
              end
              permissions_role.save
            end
          end
          # notify user by email
          UserMailer.new_information_request_comment_to_user(assigned_user, self).deliver
        end
      end
    end
  end

  # invite users to thread, if params delegate_users is present
  def process_invited_users
    if commentable.is_a?(InformationRequest)
      unless invited_users.blank?
        # obtain assigned users object
        all_invited_users = User.where(email: invited_users)
        # invite users to comment
        all_invited_users.each{|user| comment_users.create(user_id: user.id, kind: 'invited')}
        # assign permissions
        request_permissions = Permission.where(subject_class: 'InformationRequest', action: ['read','update'])
        comment_permissions = Permission.where(subject_class: 'Comment', action: 'read')
        information_request = commentable
        all_invited_users.each do |invited_user|
          unless invited_user.can?(:manage, :all) or invited_user.can?(:process_request, InformationRequest)
            # add permissions to request
            request_permissions.each do |permission|
              permissions_role = PermissionsRole.where(permission_id: permission.id, role_id: invited_user.role_id).first_or_initialize
              if permissions_role.new_record?
                permissions_role.conditions = { institution_id: Array.wrap(information_request.institution_id.to_s), id: Array.wrap(commentable_id.to_s) }
              else
                hash_conditions = permissions_role.symboled_conditions
                # Add institution_id to institution ids permission
                if hash_conditions["institution_id"]
                  hash_conditions["institution_id"] << information_request.institution_id unless Array.wrap(hash_conditions["institution_id"]).include?(information_request.institution_id)
                else
                  hash_conditions["institution_id"] = Array.wrap(information_request.institution_id)
                end
                # Add specific id to ids conditions
                if hash_conditions["id"]
                  hash_conditions["id"] << commentable_id unless hash_conditions["id"].include?(commentable_id)
                else
                  hash_conditions["id"] = Array.wrap(commentable_id)
                end
                # parse conditions ids to string
                hash_conditions["institution_id"] = Array.wrap(hash_conditions["institution_id"]).map(&:to_s)
                hash_conditions["id"] = hash_conditions["id"].map(&:to_s)
                # assign new hash conditions
                permissions_role.conditions = hash_conditions
              end
              permissions_role.save
            end
            # add permissions to comment
            comment_permissions.each do |permission|
              permissions_role = PermissionsRole.where(permission_id: permission.id, role_id: invited_user.role_id).first_or_initialize
              if permissions_role.new_record?
                permissions_role.conditions = { id: Array.wrap(self.id.to_s) }
              else
                hash_conditions = permissions_role.symboled_conditions
                # Add comment id to ids permission
                if hash_conditions["id"]
                  hash_conditions["id"] << self.id unless Array.wrap(hash_conditions["id"]).include?(self.id)
                else
                  hash_conditions["id"] = Array.wrap(self.id)
                end
                # parse conditions ids to string
                hash_conditions["id"] = Array.wrap(hash_conditions["id"]).map(&:to_s)
                # assign new hash conditions
                permissions_role.conditions = hash_conditions
              end
              permissions_role.save
            end
          end
          # notify user by email
          UserMailer.new_information_request_comment_invite_user(invited_user, self).deliver
        end
      end
    end
  end

  def destroy_assigned_users
    # eliminamos los registros
    comment_users.assigned.destroy_all
  end

  def self.process_requests
    #joins(:events).merge InformationRequestEvent.with_last_state('process', 'Comment')
    where(state: 'process')
  end

  def self.closed_requests
    #joins(:events).merge InformationRequestEvent.with_last_state('closed', 'Comment')
    where(state: 'closed')
  end

  def assigned_users_string
    @assigned_users_string ||= comment_users.assigned.map(&:user_name).join(', ')
  end

  def invited_users_string
    @invited_users_string ||= comment_users.invited.map(&:user_name).join(', ')
  end

  def process_days
    # calculate time if is a requirement and commentable is a InformationRequest
    return nil unless is_requirement? and commentable_type == 'InformationRequest'
    @status = events.order(:id)
    return nil unless @status.size > 1
    # obtain process and closed date
    process_date = created_at
    closed_date = @status.select{|s| s.state == 'closed'}.last.try(:created_at)
    return nil if process_date.nil? or closed_date.nil?
    # obtain all business days between event dates
    business_days = CalendarEvent.select('kind,start_at').joins(:institution_calendar).where('institution_calendars.institution_id = ? AND calendar_events.start_at BETWEEN ? AND ?', commentable.institution_id, process_date.beginning_of_day, closed_date.end_of_day).group_by{|o| o.start_at.to_date}
    cursor_day = process_date.to_date
    counted_days = 0
    while cursor_day <= closed_date.to_date
      if business_days.has_key?(cursor_day)
        # rest a day unless is a holiday
        counted_days += 1 unless business_days[cursor_day].map(&:kind).include?(:holiday)
      else
        # rest a day unless is weekened
        counted_days += 1 unless cursor_day.saturday? or cursor_day.sunday?
      end
      cursor_day = cursor_day + 1.day
    end
    counted_days
  end

  def closed_date
    status = events.order(:id)
    closed_date = status.select{|s| s.state == 'closed'}.last.try(:created_at)
  end

  def assigned_administrative_units
    @assigned_users_string ||= comment_users.assigned.present? ? comment_users.assigned.map(&:user_role_name).join(', ') : user.role_name
  end

  def assigned_administrative_units_emails
    @assigned_administrative_units_emails ||= comment_users.assigned.present? ? comment_users.assigned.map(&:user_email).join(', ') : user.email
  end

  def set_start_at(date = nil)
    return unless date
    begin
      nd = Date.parse(date)
      # update created at
      update_column(:created_at, Time.zone.local(nd.year, nd.month, nd.day, created_at.hour, created_at.min, created_at.sec))
      # update updated at
      update_column(:updated_at, Time.zone.local(nd.year, nd.month, nd.day, updated_at.hour, updated_at.min, updated_at.sec))
    end
  end
end
