require "counter_cache/version"
require "active_record"
require "counter_cache/backends"
require "counter_cache/counter"

module CounterCache
  extend Backends::ClassMethods
  extend ActiveSupport::Concern

  private
    def update_counts_after_create
      self.class.counters.values.each do |counter|
        counter_changes << counter.increment(self)
      end
    end

    def update_counts_after_update
      self.class.counters.values.each do |counter|
        counter_changes << counter.change(self)
      end
    end

    def update_counts_after_destroy
      self.class.counters.values.each do |counter|
        counter_changes << counter.decrement(self)
      end
    end

    def update_counts_after_commit
      counter_changes.flatten.each do |options|
        begin
          options[:counter].execute(options[:kind], options[:id]) if options
        ensure

        end
      end

      counter_changes.clear
    end

    def counter_changes
      @counter_changes ||= []
    end



    module ClassMethods
      def reset_counters(id, *association_names)
        object = find(id)
        association_names.each do |association_name|
          reflect_association = reflect_on_association(association_name)
          next unless reflect_association
          
          association = reflect_association.klass

          association.counters.each do |key, association_counter|
            count = object.send(association_name).count
            association_counter.reset(id, count) if association_counter.foreign_key == "#{self.name[0, 1].downcase + self.name[1..-1]}_#{primary_key}"
          end
        end
      end

      def counter_cache(name, options={})
        association = reflect_on_association(name)
        raise AssociationNotFound.new("No relation #{name} on #{model.name}") unless association
        association_class = association.klass
        counter = CounterCache::Counter.new(name, self, association, options)
        association_class.counters[counter.counter_name] = counter

        unless @counter_cache
          association_class.after_commit :update_counts_after_commit
          association_class.after_create :update_counts_after_create
          association_class.after_update :update_counts_after_update
          association_class.after_destroy :update_counts_after_destroy
          @counter_cache = true
        end
      end

      def counters
        @counters ||= {}
      end
    end

    class AssociationNotFound < StandardError
      def initialize(data)
        @data = data
      end
    end
end

ActiveRecord::Base.send(:include, CounterCache)
