module ArJdbc

module Tasks
  class << self

    # API similar to ActiveRecord::Tasks::DatabaseTasks on AR 4.0

    def create(config)
      tasks_instance(config).create
    end

    def drop(config)
      tasks_instance(config).drop
    end

    def purge(config)
      tasks_instance(config).purge
    end

    def charset(config)
      tasks_instance(config).charset
    end

    def collation(config)
      tasks_instance(config).collation
    end

    def structure_dump(config, filename)
      tasks_instance(config).structure_dump(filename)
    end

    def structure_load(config, filename)
      tasks_instance(config).structure_load(filename)
    end

  end
end

end

namespace :db do

class << self
  alias_method :_rails_create_database, :create_database
  alias_method :_rails_drop_database,   :drop_database
end

def create_database(config)
  case config['adapter']
  when /mysql2/
    unless defined? Mysql2::Error
      # NOTE: fake it for create_database(config)
      Object.const_set :Mysql2, Module.new
      Mysql2.const_set :Error, ActiveRecord::JDBCError
      ActiveRecord::JDBCError.class_eval do
        def error; self end # Mysql2::Error#error
      end
    end
    _rails_create_database adapt_jdbc_config(config)
  when /mysql/
    unless defined? Mysql::Error
      # NOTE: fake it for create_database(config)
      Object.const_set :Mysql, Module.new
      Mysql.const_set :Error, ActiveRecord::JDBCError
      ActiveRecord::JDBCError.class_eval do
        def error; self end # Mysql::Error#error
      end
    end
    _rails_create_database adapt_jdbc_config(config)
  when /postgresql|sqlite/
    _rails_create_database adapt_jdbc_config(config)
  else
    ArJdbc::Tasks.create(config)
  end
end

def drop_database(config)
  case config['adapter']
  when /mysql|postgresql|sqlite/
    _rails_drop_database adapt_jdbc_config(config)
  else
    ArJdbc::Tasks.drop(config)
  end
end

redefine_task :charset do # available on 2.3
  ArJdbc::Tasks.charset ActiveRecord::Base.configurations[rails_env]
end

redefine_task :collation do # available on 2.3
  ArJdbc::Tasks.collation ActiveRecord::Base.configurations[rails_env]
end

namespace :structure do

  redefine_task :dump do
    config = ActiveRecord::Base.configurations[rails_env] # current_config
    filename = structure_sql

    case config['adapter']
    when /mysql/
      ActiveRecord::Base.establish_connection(config)
      File.open(filename, 'w:utf-8') { |f| f << ActiveRecord::Base.connection.structure_dump }
    when /postgresql/
      ActiveRecord::Base.establish_connection(config)

      ENV['PGHOST'] = config['host'] if config['host']
      ENV['PGPORT'] = config['port'].to_s if config['port']
      ENV['PGPASSWORD'] = config['password'].to_s if config['password']
      ENV['PGUSER'] = config['username'].to_s if config['username']

      require 'shellwords'
      search_path = config['schema_search_path']
      unless search_path.blank?
        search_path = search_path.split(",").map{ |part| "--schema=#{Shellwords.escape(part.strip)}" }.join(" ")
      end
      sh "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(config['database'])}"

      File.open(filename, 'a') { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
    when /sqlite/
      dbfile = config['database']
      sh "sqlite3 #{dbfile} .schema > #{filename}"
    else
      ActiveRecord::Base.establish_connection(config)
      ArJdbc::Tasks.structure_dump(config, filename)
    end

    if ActiveRecord::Base.connection.supports_migrations?
      File.open(filename, 'a') { |f| f << ActiveRecord::Base.connection.dump_schema_information }
    end

  end

  redefine_task :load do
    config = current_config
    filename = structure_sql

    case config['adapter']
    when /mysql/
      ActiveRecord::Base.establish_connection(config)
      ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
      IO.read(filename).split("\n\n").each do |table|
        ActiveRecord::Base.connection.execute(table)
      end
    when /postgresql/
      ENV['PGHOST'] = config['host'] if config['host']
      ENV['PGPORT'] = config['port'].to_s if config['port']
      ENV['PGPASSWORD'] = config['password'].to_s if config['password']
      ENV['PGUSER'] = config['username'].to_s if config['username']

      %x`psql -f "#{filename}" #{config['database']}`
    when /sqlite/
      dbfile = config['database']
      %x`sqlite3 #{dbfile} < "#{filename}"`
    else
      ArJdbc::Tasks.structure_load(config, filename)
    end
  end

  def structure_sql
    ENV['DB_STRUCTURE'] ||= begin
      root = defined?(Rails.root) ? Rails.root : ( RAILS_ROOT rescue nil )
      if ActiveRecord::VERSION::STRING > '3.2'
        root ? File.join(root, "db", "structure.sql") : File.join("db", "structure.sql")
      else
        root ? File.join(root, "db/#{rails_env}_structure.sql") : "db/#{rails_env}_structure.sql"
      end
    end
  end

end

namespace :test do

  # desc "Recreate the test database from an existent structure.sql file"
  redefine_task :load_structure => 'db:test:purge' do # not on 2.3
    begin
      current_config(:config => ActiveRecord::Base.configurations['test'])
      Rake::Task["db:structure:load"].invoke
    ensure
      current_config(:config => nil)
    end
  end

  # desc "Recreate the test database from a fresh structure.sql file"
  redefine_task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
  # same as on 3.2 - but this task gets changed on 2.3 by depending on :load_structure

  # desc "Empty the test database"
  redefine_task :purge do
    config = ActiveRecord::Base.configurations['test']
    case config['adapter']
    when /mysql/
      ActiveRecord::Base.establish_connection(:test)
      options = mysql_creation_options(config) rescue config
      ActiveRecord::Base.connection.recreate_database(config['database'], options)
    when /postgresql/
      ActiveRecord::Base.clear_active_connections!
      # drop_database(config) :
      ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
      ActiveRecord::Base.connection.drop_database config['database']
      # create_database(config) :
      encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
      ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => encoding))
    when /sqlite/
      dbfile = config['database']
      File.delete(dbfile) if File.exist?(dbfile)
    else
      ArJdbc::Tasks.purge(config)
    end
  end
  # only does (:purge => :environment) on AR < 3.2
  task :purge => :load_config if Rake::Task.task_defined?(:load_config)

end

end