# frozen_string_literal: true

require "factory_bot"
require "active_record"

module FactoryBot
  module Preload
    class << self
      attr_accessor :preloaders, :factories, :record_ids, :clean_with,
                    :default_helper_name, :helper_name, :reserved_tables
    end

    self.preloaders = []
    self.factories = {}
    self.record_ids = {}
    self.clean_with = :truncation
    self.default_helper_name = ->(_class_name, helper_name) { helper_name }
    self.helper_name = default_helper_name
    self.reserved_tables = %w[
      ar_internal_metadata
      schema_migrations
    ]

    require "factory_bot/preload/helpers"
    require "factory_bot/preload/version"
    require "factory_bot/preload/rspec" if defined?(RSpec)
    require "factory_bot/preload/minitest" if defined?(Minitest)
    require "factory_bot/preload/extension"

    ActiveSupport.on_load(:after_initialize) do
      ::FactoryBot::Preload::Helpers.load_models
      ::FactoryBot::SyntaxRunner.include ::FactoryBot::Preload::Helpers
    end

    def self.active_record
      ActiveRecord::Base
    end

    def self.connection
      active_record.connection
    end

    def self.run
      helper = Object.new.extend(Helpers)

      connection.transaction requires_new: true do
        preloaders.each do |block|
          helper.instance_eval(&block)
        end
      end
    end

    def self.clean(*names)
      query = case clean_with
              when :truncation
                try_truncation_query
              when :deletion
                "DELETE FROM %s"
              else
                raise "Couldn't find #{clean_with} clean type"
              end

      names = active_record_names if names.empty?

      connection.disable_referential_integrity do
        names.each do |table|
          connection.execute(query % connection.quote_table_name(table))
        end
      end
    end

    def self.active_record_names
      connection.tables.reject {|name| reserved_tables.include?(name) }
    end

    def self.reload_factories
      factories.each do |class_name, group|
        group.each do |name, _factory|
          factories[class_name][name] = nil
        end
      end
    end

    def self.try_truncation_query
      case connection.adapter_name
      when "SQLite"
        "DELETE FROM %s"
      when "PostgreSQL"
        "TRUNCATE TABLE %s RESTART IDENTITY CASCADE"
      else
        "TRUNCATE TABLE %s"
      end
    end
  end
end
