# require File.expand_path(File.dirname(__FILE__)) + '/base'

module Socialization
  module RedisStores
    class Like < Socialization::RedisStores::Base
      extend Socialization::Stores::Mixins::Base
      extend Socialization::Stores::Mixins::Like
      extend Socialization::RedisStores::Mixins::Base

      class << self
        def like!(liker, likeable)
          unless likes?(liker, likeable)
            Socialization.redis.sadd generate_likers_key(liker, likeable), liker.id
            Socialization.redis.sadd generate_likeables_key(liker, likeable), likeable.id

            call_after_create_hooks(liker, likeable)
            liker.touch if [:all, :liker].include?(touch) && liker.respond_to?(:touch)
            likeable.touch if [:all, :likeable].include?(touch) && likeable.respond_to?(:touch)
            true
          else
            false
          end
        end

        def unlike!(liker, likeable)
          if likes?(liker, likeable)
            Socialization.redis.srem generate_likers_key(liker, likeable), liker.id
            Socialization.redis.srem generate_likeables_key(liker, likeable), likeable.id

            call_after_destroy_hooks(liker, likeable)
            liker.touch if [:all, :liker].include?(touch) && liker.respond_to?(:touch)
            likeable.touch if [:all, :likeable].include?(touch) && likeable.respond_to?(:touch)
            true
          else
            false
          end
        end

        def likes?(liker, likeable)
          Socialization.redis.sismember generate_likers_key(liker, likeable), liker.id
        end

        # Returns an ActiveRecord::Relation of all the likers of a certain type that are likeing likeable
        def likers_relation(likeable, klass, opts = {})
          ids = likers(likeable, klass, :pluck => :id)
          klass.where('id IN (?)', ids)
        end

        # Returns all the likers of a certain type that are likeing likeable
        def likers(likeable, klass, opts = {})
          if opts[:pluck]
            Socialization.redis.smembers(generate_likers_key(klass, likeable)).map { |id|
              id.to_i if id.is_integer?
            }
          else
            likers_relation(likeable, klass, opts).all
          end
        end

        # Returns an ActiveRecord::Relation of all the likeables of a certain type that are liked by liker
        def likeables_relation(liker, klass, opts = {})
          ids = likeables(liker, klass, :pluck => :id)
          klass.where('id IN (?)', ids)
        end

        # Returns all the likeables of a certain type that are liked by liker
        def likeables(liker, klass, opts = {})
          if opts[:pluck]
            Socialization.redis.smembers(generate_likeables_key(liker, klass)).map { |id|
              id.to_i if id.is_integer?
            }
          else
            likeables_relation(liker, klass, opts).all
          end
        end

        def touch(what = nil)
          if what.nil?
            @touch || false
          else
            raise ArgumentError unless [:all, :liker, :likeable, false, nil].include?(what)
            @touch = what
          end
        end

        def after_like(method)
          raise ArgumentError unless method.is_a?(Symbol) || method.nil?
          @after_create_hook = method
        end

        def after_unlike(method)
          raise ArgumentError unless method.is_a?(Symbol) || method.nil?
          @after_destroy_hook = method
        end

      private
        def call_after_create_hooks(liker, likeable)
          self.send(@after_create_hook, liker, likeable) if @after_create_hook
        end

        def call_after_destroy_hooks(liker, likeable)
          self.send(@after_destroy_hook, liker, likeable) if @after_destroy_hook
        end

        def generate_likers_key(liker, likeable)
          raise ArgumentError.new("`likeable` needs to be an acts_as_likeable objecs, not a class.") if likeable.class == Class
          liker_class = if liker.class == Class
            liker
          else
            liker.class
          end

          "Likers:#{likeable.class}:#{likeable.id}:#{liker_class}"
        end

        def generate_likeables_key(liker, likeable)
          raise ArgumentError.new("`liker` needs to be an acts_as_liker object, not a class.") if liker.class == Class
          likeable_class = if likeable.class == Class
            likeable
          else
            likeable.class
          end

          "Likeables:#{liker.class}:#{liker.id}:#{likeable_class}"
        end
      end # class << self

    end
  end
end