module EasyPatch
  module JournalPatch

    def self.included(base)
      base.extend(ClassMethods)
      base.send(:include, InstanceMethods)

      base.class_eval do

        has_many :easy_permissions, :as => :entity, :class_name => 'EasyPermission'

        include EasyExtensions::EasyInlineFragmentStripper
        html_fragment :notes, :scrub => :strip
        strip_inline_images :notes

        attr_accessor :notify_children

        before_save :cancel_save
        after_initialize :default_values
        # after_initialize :gsub_note_from_textile
        after_create :copy_journal_to_parent_and_children, :if => Proc.new{|journal|
          journal.issue.present? && !journal.issue.mass_operations_in_progress && journal.notes.present? }
        after_commit :send_notification, on: :create

        # Journal must be commited because of delay send
        skip_callback :create, :after, :send_notification

        alias_method_chain :cache_key, :easy_extensions
        alias_method_chain :notified_users, :easy_extensions
        alias_method_chain :notified_watchers, :easy_extensions
        alias_method_chain :send_notification, :easy_extensions
        alias_method_chain :journalize_changes, :easy_extensions
        alias_method_chain :visible_details, :easy_extensions

        scope :with_notes, -> { where("LENGTH(notes) > 0") }

        # change acts_as_event params
        Journal.event_options = {
                       :datetime => :created_on,
                       :title => Proc.new {|o| o.easy_journal_option(:title) },
                       :description => :notes,
                       :author => :user,
                       :type => Proc.new {|o| o.easy_journal_option(:type) },
                       :url => Proc.new {|o| o.easy_journal_option(:url) },
                       :group => :easy_journal_event_group
                     }

        def default_values
          if new_record?
            begin; self.created_on ||= DateTime.now; rescue; end
          end
        end

        def cancel_save
          false if self.project && self.project.easy_is_easy_template?
        end

        def copy_journal_to_parent_and_children
          issues_for_copy = []
          if self.issue.parent && self.issue.parent.tracker.easy_distributed_tasks? && !self.issue.tracker.easy_distributed_tasks?
            issues_for_copy << self.issue.parent
            issues_for_copy.concat(self.issue.siblings)
          elsif self.issue.tracker.easy_distributed_tasks?
            issues_for_copy.concat(self.issue.children)
          end

          issues_for_copy.each{|i| self.copy_to_issue(i)}
        end

        def copy_to_issue(i)
          self_copy = dup
          self_copy.issue = i
          self_copy.issue.mass_operations_in_progress = true
          self_copy.save
        end

        def important_details_map
          important_columns = self.journalized.journalized_options[:important_columns]
          self.visible_details.map { |detail| (detail.property == 'attr' && important_columns.include?(detail.prop_key)) || detail.property == 'attachment' }
        end

        def not_important_details?
          self.important_details_map.include?(false)
        end

        def important_details?
          self.important_details_map.include?(true)
        end

        private

        def attach_stripped_image(filename, file, extension)
          journalized.attachments.create(
              :file => file,
              :filename => filename,
              :author => User.current,
              :description => !!EasySetting.value('attachment_description_required') && '*' || '',
              :content_type => "image/#{extension}") if journalized.respond_to?(:attachments) && journalized.attachments.respond_to?(:create)
        end

      end
    end

    module InstanceMethods

      def cache_key_with_easy_extensions
        if new_record?
          'journals/new'
        else
          "journals/#{id}-#{created_on.strftime('%Y%m%d%H%M%S')}"
        end
      end

      def notified_users_with_easy_extensions
        notified = notified_users_without_easy_extensions

        additional_notified_filter(notified)
      end

      def notified_watchers_with_easy_extensions
        notified = notified_watchers_without_easy_extensions

        additional_notified_filter(notified)
      end

      def send_notification_with_easy_extensions
        return unless self.journalized.is_a?(Issue)
        return if self.user && self.user.pref.no_notification_ever

        if self.notify? &&
            (Setting.notified_events.include?('issue_updated') ||
              (Setting.notified_events.include?('issue_note_added') && self.notes.present?) ||
              (Setting.notified_events.include?('issue_status_updated') && self.new_status.present?) ||
              (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) ||
              (Setting.notified_events.include?('issue_priority_updated') && self.new_value_for('priority_id').present?)
          )
          Mailer.send_mail_issue_edit(self)
          self.journalized.notification_sent = true
        end
      end

      def visible_details_with_easy_extensions(user = User.current)
        visible_details_without_easy_extensions(user).reject do |detail|
          !detail.prop_key.in?(journalized.journalized_attribute_names) if detail.property == 'attr'
        end
      end

      def easy_journal_event_group
        if journalized && journalized.respond_to?(:easy_journal_event_group)
          return journalized.easy_journal_event_group
        end
        :issue
      end

      def easy_journal_option(option)
        if journalized && journalized.respond_to?(:easy_journal_option)
          return journalized.easy_journal_option(option, self)
        end
        ''
      end

      def additional_notified_filter(notified = [])
        if notify_children && issue
          notified = issue.children.map{|i| (i.assigned_to.is_a?(Group) ? i.assigned_to.users : i.assigned_to) }
          notified = notified.flatten.select{|user| user && user.notify_about?(issue) }
          notified.concat(issue.notified_users)
          notified.concat(issue.notified_watchers)
          notified.flatten!
          notified.compact!
          notified.uniq!(&:id)
          notified
        else
          return notified unless self.details.count == 1

          details = self.details.first
          if details.property == 'relation' && details.value.present?
            entity = self.journalized_type.constantize.find(details.value)
            notified = notified.select{|user| entity.visible?(user)}
          end
        end
        notified
      rescue ActiveRecord::RecordNotFound
        notified
      end

      private

        def journalize_changes_with_easy_extensions
          # attributes changes
          if @attributes_before_change
            journalized.journalized_attribute_names.each {|attribute|
              before = @attributes_before_change[attribute]
              after = journalized.send(attribute)
              next if before == after || (before.blank? && after.blank?) || (attribute.to_s == 'description' && Sanitize.clean(before.to_s, :output => :html).strip == Sanitize.clean(after.to_s, :output => :html).strip)
              add_attribute_detail(attribute, before, after)
            }
          end
          if @custom_values_before_change
            # custom fields changes
            journalized.custom_field_values.each {|c|
              next if c.custom_field.field_format == 'easy_rating'
              before = @custom_values_before_change[c.custom_field_id]
              after = c.value
              next if before == after || (before.blank? && after.blank?)

              if before.is_a?(Array) || after.is_a?(Array)
                before = [before] unless before.is_a?(Array)
                after = [after] unless after.is_a?(Array)

                # values removed
                (before - after).reject(&:blank?).each do |value|
                  add_custom_value_detail(c, value, nil)
                end
                # values added
                (after - before).reject(&:blank?).each do |value|
                  add_custom_value_detail(c, nil, value)
                end
              else
                add_custom_value_detail(c, before, after)
              end
            }
          end
          start
        end

    end

    module ClassMethods

      def easy_activity_custom_project_scope(scope, options, event_type)
        entity_class = begin; event_type.singularize.camelcase.constantize rescue nil; end
        if entity_class
          scope.where("#{entity_class.table_name}.project_id in (?)", options[:project_ids])
        else
          self.none
        end
      end

    end

  end

end
EasyExtensions::PatchManager.register_model_patch 'Journal', 'EasyPatch::JournalPatch'
