begin
  require 'active_record'
rescue LoadError
  # :nocov:
  abort "ActiveRecord requires the rails appraisal. Try `appraisal cucumber`"
  # :nocov:
end

require 'yaml'

module Eaco
  module Cucumber

    ##
    # +ActiveRecord+ configuration and connection.
    #
    # Database configuration is looked up in +features/active_record.yml+ by
    # default. Logs are sent to +features/active_record.log+, truncating the
    # file at each run.
    #
    # Environment variables:
    #
    # * +EACO_AR_CONFIG+ specify a different +ActiveRecord+ configuration file
    # * +VERBOSE+ log to +stderr+
    #
    module ActiveRecord
      autoload :Document,   'eaco/cucumber/active_record/document'   # Resource
      autoload :User,       'eaco/cucumber/active_record/user'       # Actor
      autoload :Department, 'eaco/cucumber/active_record/department' # Designator source
      autoload :Position,   'eaco/cucumber/active_record/position'   # Designator source

      extend self

      ##
      # Looks up ActiveRecord and sets the +logger+.
      #
      # @return [Class] +ActiveRecord::Base+
      #
      def active_record
        @_active_record ||= ::ActiveRecord::Base.tap do |active_record|
          active_record.logger = ::Logger.new(active_record_log).tap {|l| l.level = 0}
        end
      end

      ##
      # Log to stderr if +VERBOSE+ is given, else log to
      # +features/active_record.log+
      #
      # @return [IO] the log destination
      #
      def active_record_log
        @_active_record_log ||= ENV['VERBOSE'] ? $stderr :
          'features/active_record.log'.tap {|f| File.open(f, "w+")}
      end

      ##
      # @return [Logger] the logger configured, logging to {.active_record_log}.
      #
      def logger
        active_record.logger
      end

      ##
      # Returns an Hash wit the database configuration.
      #
      # Caveat:the returned +Hash+ has a custom +.to_s+ method that formats
      # the configuration as a +pgsql://+ URL.
      #
      # @return [Hash] the current database configuration
      #
      # @see {#config_file}
      #
      def configuration
        @_config ||= YAML.load(config_file.read).tap do |conf|
          def conf.to_s
            # :nocov:
            'pgsql://%s:%s@%s/%s' % values_at(
              :username, :password, :hostname, :database
            )
            # :nocov:
          end
        end
      end

      ##
      # @return [Pathname] the currently configured configuration file. Override
      #                    using the +EACO_AR_CONFIG' envinronment variable.
      #
      def config_file
        Pathname.new(ENV['EACO_AR_CONFIG'] || default_config_file)
      end

      ##
      # @return [String] +active_record.yml+ relative to this source file.
      #
      # @raise [Errno::ENOENT] if the configuration file is not found.
      #
      # :nocov:
      # This isn't ran by Travis as we set EACO_AR_CONFIG, so Coveralls raises
      # a false positive.
      def default_config_file
        Pathname.new('features/active_record.yml').realpath

      rescue Errno::ENOENT => error
        raise error.class.new, <<-EOF.squeeze(' ')

          #{error.message}.

          Please define your Active Record database configuration in the
          default location, or specify your configuration file location by
          passing the `EACO_AR_CONFIG' environment variable.
        EOF
      end
      # :nocov:

      ##
      # Establish ActiveRecord connection using the given configuration hash
      #
      # @param config [Hash] the configuration to use, {#configuration} by default.
      #
      # @return [ActiveRecord::ConnectionAdapters::ConnectionPool]
      #
      # @raise [ActiveRecord::ActiveRecordError] if cannot connect
      #
      def connect!(config = self.configuration)
        unless ENV['VERBOSE']
          config = config.merge(min_messages: 'WARNING')
        end

        active_record.establish_connection(config)
      end

      ##
      # Loads the defined {ActiveRecord#schema}
      #
      # @return [nil]
      #
      def define_schema!
        log_stdout { load 'eaco/cucumber/active_record/schema.rb' }
      end

      protected

      ##
      # Captures stdout emitted by the given +block+ and logs it
      # as +info+ messages.
      #
      # @param block [Proc]
      # @return [nil]
      # @see {Rake::Utils.capture_stdout}
      #
      def log_stdout(&block)
        stdout = Rake::Utils.capture_stdout(&block)

        stdout.split("\n").each do |line|
          logger.info line
        end

        nil
      end
    end

  end
end
