require 'yaml'
require 'active_record'
require 'gemika/env'
require 'gemika/errors'

module Gemika
  ##
  # Helpers for creating a test database.
  #
  class Database

    class Error < StandardError; end
    class UnknownAdapter < Error; end

    def initialize(options = {})
      yaml_config_folder = options.fetch(:config_folder, 'spec/support')
      yaml_config_filename = if Env.github?
        'database.github.yml'
      else
        'database.yml'
      end
      yaml_config_path = File.join(yaml_config_folder, yaml_config_filename)
      if File.exist?(yaml_config_path)
        @yaml_config = YAML.load_file(yaml_config_path)
      else
        @yaml_config = {}
        warn "No database configuration in #{yaml_config_path}, using defaults: #{adapter_config.inspect}"
      end
      @connected = false
    end

    ##
    # Connects ActiveRecord to the database configured in `spec/support/database.yml`.
    #
    def connect
      unless @connected
        ActiveRecord::Base.establish_connection(**adapter_config)
        @connected = true
      end
    end

    ##
    # Drops all tables from the current database.
    #
    def drop_tables!
      connect
      connection.tables.each do |table|
        connection.drop_table table
      end
    end

    ##
    # Runs the [ActiveRecord database migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html) described in `block`.
    #
    # @example
    #   Gemika::Database.new.migrate do
    #     create_table :users do |t|
    #       t.string :name
    #       t.string :email
    #       t.string :city
    #     end
    #  end
    def migrate(&block)
      connect
      ActiveRecord::Migration.class_eval(&block)
    end

    ##
    # Drops all tables,  then
    # runs the [ActiveRecord database migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html) described in `block`.
    #
    # @example
    #   Gemika::Database.new.rewrite_schema! do
    #     create_table :users do |t|
    #       t.string :name
    #       t.string :email
    #       t.string :city
    #     end
    #  end
    def rewrite_schema!(&block)
      connect
      drop_tables!
      migrate(&block)
    end

    ##
    # Returns a hash of ActiveRecord adapter options for the currently activated database gem.
    #
    def adapter_config
      default_config = {}
      default_config['database'] = guess_database_name
      if Env.gem?('pg')
        default_config['adapter'] = 'postgresql'
        default_config['password'] = ''
        user_config = @yaml_config['postgresql'] || @yaml_config['postgres'] || @yaml_config['pg'] || {}
      elsif Env.gem?('mysql2')
        default_config['adapter'] = 'mysql2'
        default_config['encoding'] = 'utf8'
        user_config = (@yaml_config['mysql'] || @yaml_config['mysql2']) || {}
      elsif Env.gem?('sqlite3')
        default_config['adapter'] = 'sqlite3'
        default_config['database'] = ':memory:'
        user_config = (@yaml_config['sqlite'] || @yaml_config['sqlite3']) || {}
      else
        raise UnknownAdapter, "Unknown database type. Either 'pg', 'mysql2', or 'sqlite3' gem should be in your current bundle."
      end
      default_config.merge(user_config).symbolize_keys
    end

    private

    def guess_database_name
      project_name = File.basename(Dir.pwd)
      "#{project_name}_test"
    end

    def connection
      ActiveRecord::Base.connection
    end

  end
end
