# frozen_string_literal: true

require 'pathname'

class ThinkingSphinx::Configuration < Riddle::Configuration
  attr_accessor :configuration_file, :indices_location, :version, :batch_size
  attr_reader :index_paths
  attr_writer :controller, :index_set_class, :indexing_strategy,
    :guarding_strategy

  delegate :environment, :to => :framework

  @@mutex = defined?(ActiveSupport::Concurrency::LoadInterlockAwareMonitor) ?
    ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new : Mutex.new

  def initialize
    super

    reset
  end

  def self.instance
    @instance ||= new
  end

  def self.reset
    @instance = nil
  end

  def bin_path
    settings['bin_path']
  end

  def controller
    @controller ||= begin
      rc = Riddle::Controller.new self, configuration_file
      rc.bin_path = bin_path.gsub(/([^\/])$/, '\1/') if bin_path.present?
      rc
    end
  end

  def framework
    @framework ||= ThinkingSphinx::Frameworks.current
  end

  def framework=(framework)
    @framework = framework
    reset
    framework
  end

  def engine_index_paths
    return [] unless defined?(Rails)

    engine_indice_paths.flatten.compact.sort
  end

  def engine_indice_paths
    Rails::Engine.subclasses.collect(&:instance).collect do |engine|
      engine.paths.add 'app/indices' unless engine.paths['app/indices']
      engine.paths['app/indices'].existent
    end
  end

  def guarding_strategy
    @guarding_strategy ||= ThinkingSphinx::Guard::Files
  end

  def index_set_class
    @index_set_class ||= ThinkingSphinx::IndexSet
  end

  def indexing_strategy
    @indexing_strategy ||= ThinkingSphinx::IndexingStrategies::AllAtOnce
  end

  def indices_for_references(*references)
    index_set_class.new(:references => references).to_a
  end

  def next_offset(reference)
    @offsets[reference] ||= @offsets.keys.count
  end

  def preload_indices
    @@mutex.synchronize do
      return if @preloaded_indices

      index_paths.each do |path|
        Dir["#{path}/**/*.rb"].sort.each { |file| preload_index file }
      end

      normalise
      verify

      @preloaded_indices = true
    end
  end

  def preload_index(file)
    if ActiveRecord::VERSION::MAJOR <= 5
      ActiveSupport::Dependencies.require_or_load file
    else
      load file
    end
  end

  def render
    preload_indices

    super
  end

  def render_to_file
    unless settings['skip_directory_creation'] || searchd.binlog_path.blank?
      FileUtils.mkdir_p searchd.binlog_path
    end

    open(configuration_file, 'w') { |file| file.write render }
  end

  def settings
    @settings ||= ThinkingSphinx::Settings.call self
  end

  def setup
    @configuration_file = settings['configuration_file']
    @index_paths = engine_index_paths +
      [Pathname.new(framework.root).join('app', 'indices').to_s]
    @indices_location = settings['indices_location']
    @version = settings['version'] || '2.2.11'
    @batch_size = settings['batch_size'] || 1000

    if settings['common_sphinx_configuration']
      common.common_sphinx_configuration  = true
      indexer.common_sphinx_configuration = true
    end

    configure_searchd

    apply_sphinx_settings!

    @offsets = {}
  end

  private

  def apply_sphinx_settings!
    sphinx_sections.each do |object|
      settings.each do |key, value|
        next unless object.class.settings.include?(key.to_sym)

        object.send("#{key}=", value)
      end
    end
  end

  def configure_searchd
    searchd.socket = "#{settings["socket"]}:mysql41" if socket?

    if tcp?
      searchd.address = settings['address'].presence || Defaults::ADDRESS
      searchd.mysql41 = settings['mysql41'] || settings['port'] || Defaults::PORT
    end

    searchd.mysql_version_string = '5.5.21' if RUBY_PLATFORM == 'java'
  end

  def normalise
    if settings['distributed_indices'].nil? || settings['distributed_indices']
      ThinkingSphinx::Configuration::DistributedIndices.new(indices).reconcile
    end

    ThinkingSphinx::Configuration::ConsistentIds.new(indices).reconcile
    ThinkingSphinx::Configuration::MinimumFields.new(indices).reconcile
  end

  def reset
    @settings = nil
    setup
  end

  def socket?
    settings["socket"].present?
  end

  def sphinx_sections
    sections = [indexer, searchd]
    sections.unshift common if settings['common_sphinx_configuration']
    sections
  end

  def tcp?
    settings["socket"].nil?      ||
    settings["address"].present? ||
    settings["mysql41"].present? ||
    settings["port"].present?
  end

  def verify
    ThinkingSphinx::Configuration::DuplicateNames.new(indices).reconcile
  end
end

require 'thinking_sphinx/configuration/consistent_ids'
require 'thinking_sphinx/configuration/defaults'
require 'thinking_sphinx/configuration/distributed_indices'
require 'thinking_sphinx/configuration/duplicate_names'
require 'thinking_sphinx/configuration/minimum_fields'
