module RedmineCrm
  module ActsAsTaggable #:nodoc:
    class Tag < ActiveRecord::Base #:nodoc:
      has_many :taggings, :dependent => :destroy

      validates_presence_of :name
      validates :name, :uniqueness => { :message => " not uniq tag" }
      validates :name, :presence => true
      cattr_accessor :destroy_unused
      self.destroy_unused = false

      attr_accessible :name if defined?(ActiveModel::MassAssignmentSecurity)

      # LIKE is used for cross-database case-insensitivity
      def self.find_or_create_with_like_by_name(name)
        # find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
        where("LOWER(name) LIKE LOWER(?)", name).first || create(:name => name)
      end

      def ==(object)
        super || (object.is_a?(Tag) && name == object.name)
      end

      def to_s
        name
      end

      def count
        read_attribute(:count).to_i
      end

      class << self
        # Calculate the tag counts for all tags.
        #  :start_at - Restrict the tags to those created after a certain time
        #  :end_at - Restrict the tags to those created before a certain time
        #  :conditions - A piece of SQL conditions to add to the query
        #  :limit - The maximum number of tags to return
        #  :order - A piece of SQL to order by. Eg 'count desc' or 'taggings.created_at desc'
        #  :at_least - Exclude tags with a frequency less than the given value
        #  :at_most - Exclude tags with a frequency greater than the given value
        def counts(options = {})
          opt = options_for_counts(options)
          select(opt[:select]).where(opt[:conditions]).joins(opt[:joins]).group(opt[:group])
        end

        def options_for_counts(options = {})
          options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :joins
          options = options.dup

          start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
          end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]

          conditions = [
            (sanitize_sql(options.delete(:conditions)) if options[:conditions]),
            start_at,
            end_at
          ].compact

          conditions = conditions.join(' AND ') if conditions.any?

          joins = ["INNER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
          joins << options.delete(:joins) if options[:joins]

          at_least  = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
          at_most   = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
          having    = 'COUNT(*) > 0'
          having    = [having, at_least, at_most].compact.join(' AND ')
          group_by  = "#{Tag.table_name}.id, #{Tag.table_name}.name"
          # group_by << " AND #{having}" unless having.blank?

          { :select     => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
            :joins      => joins.join(" "),
            :conditions => conditions,
            :group      => group_by,
            :having     => having
          }.update(options)
        end
      end
    end
  end
end