class SavedSearch < ApplicationRecord
  module ListbooruMethods
    extend ActiveSupport::Concern

    module ClassMethods
      def enabled?
        Danbooru.config.aws_sqs_saved_search_url.present?
      end

      def posts_search_available?
        enabled? && CurrentUser.is_gold?
      end

      def sqs_service
        SqsService.new(Danbooru.config.aws_sqs_saved_search_url)
      end

      def post_ids(user_id, label = nil)
        return [] unless enabled?
        label = normalize_label(label) if label

        Cache.get("ss-#{user_id}-#{Cache.hash(label)}", 60) do
          queries = SavedSearch.queries_for(user_id, label)
          return [] if queries.empty?

          json = {
            "key" => Danbooru.config.listbooru_auth_key,
            "queries" => queries
          }.to_json

          uri = "#{Danbooru.config.listbooru_server}/v2/search"

          resp = HTTParty.post(uri, Danbooru.config.httparty_options.merge(body: json))
          if resp.success?
            resp.body.to_s.scan(/\d+/).map(&:to_i)
          else
            raise "HTTP error code: #{resp.code} #{resp.message}"
          end
        end
      end
    end
  end

  include ListbooruMethods

  belongs_to :user
  validates :query, :presence => true
  validate :validate_count
  attr_accessible :query, :label_string
  before_create :update_user_on_create
  after_destroy :update_user_on_destroy
  after_save {|rec| Cache.delete(SavedSearch.cache_key(rec.user_id))}
  after_destroy {|rec| Cache.delete(SavedSearch.cache_key(rec.user_id))}
  before_validation :normalize
  scope :labeled, lambda {|label| where("labels @> string_to_array(?, '~~~~')", label)}

  def self.normalize_label(label)
    label.to_s.strip.downcase.gsub(/[[:space:]]/, "_")
  end

  def self.search_labels(user_id, params)
    labels = labels_for(user_id)

    if params[:label].present?
      query = Regexp.escape(params[:label]).gsub("\\*", ".*")
      query = ".*#{query}.*" unless query.include?("*")
      query = /\A#{query}\z/
      labels = labels.grep(query)
    end

    labels
  end

  def self.labels_for(user_id)
    Cache.get(cache_key(user_id)) do
      SavedSearch.where(user_id: user_id).order("label").pluck("distinct unnest(labels) as label")
    end
  end

  def self.cache_key(user_id)
    "ss-labels-#{user_id}"
  end

  def self.queries_for(user_id, label = nil, options = {})
    if label
      SavedSearch.where(user_id: user_id).labeled(label).map(&:normalized_query).sort.uniq
    else
      SavedSearch.where(user_id: user_id).map(&:normalized_query).sort.uniq
    end
  end

  def normalized_query
    Tag.normalize_query(query, sort: true)
  end

  def normalize
    self.query = Tag.normalize_query(query, sort: false)
    self.labels = labels.map {|x| SavedSearch.normalize_label(x)}.reject {|x| x.blank?}
  end

  def validate_count
    if user.saved_searches.count + 1 > user.max_saved_searches
      self.errors[:user] << "can only have up to #{user.max_saved_searches} " + "saved search".pluralize(user.max_saved_searches)
    end
  end

  def update_user_on_create
    if !user.has_saved_searches?
      user.update_attribute(:has_saved_searches, true)
    end
  end

  def update_user_on_destroy
    if user.saved_searches.count == 0
      user.update_attribute(:has_saved_searches, false)
    end
  end

  def label_string
    labels.join(" ")
  end

  def label_string=(val)
    self.labels = val.to_s.split(/[[:space:]]+/)
  end

  def labels=(labels)
    labels = labels.map { |label| SavedSearch.normalize_label(label) }
    super(labels)
  end
end
