require 'yaml'
require 'active_record'

namespace :disco do
  namespace :db do
    def disco_database_configuration(disco_config, database)
      disco_config.reduce({}) do |map, (key, value)|
        if value.is_a?(Hash) && value.has_key?(database)
          map[key] = value[database]
        end
        map
      end
    end

    task :load_config => :environment do
      migrate_path = 'db/migrate'
      ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths['db'].first
      ActiveRecord::Tasks::DatabaseTasks.seed_loader = Rails.application
      ActiveRecord::Base.clear_all_connections!

      if ENV['SYSTEM'] == 'domain'
        migrate_path = 'db/migrate_domain'
        ActiveRecord::Tasks::DatabaseTasks.env = ENV['DOMAIN_ENV'] || Rails.env
        disco_config = YAML.load_file(File.join Rails.root, 'config', 'disco.yml')
        ActiveRecord::Tasks::DatabaseTasks.database_configuration = disco_database_configuration(disco_config, 'domain_database')
        ActiveRecord::Tasks::DatabaseTasks.migrations_paths =
            Array(ENV['DOMAIN_MIGRATIONS_DIR'] || Rails.application.paths[migrate_path] || File.join(Rails.root, migrate_path))
        ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures_domain'
        ENV['SCHEMA'] = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema_domain.rb')
        ENV['DB_STRUCTURE'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'structure_domain.sql')

      else # 'projection' - normal rails
        ActiveRecord::Tasks::DatabaseTasks.env = ENV['PROJECTION_ENV'] || Rails.env
        ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
        ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Array(Rails.application.paths[migrate_path])
        ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
        ENV['SCHEMA'] = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
        ENV['DB_STRUCTURE'] = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'structure.sql')
      end

      if defined?(ENGINE_PATH) && (engine = Rails::Engine.find(ENGINE_PATH))
        if engine.paths[migrate_path].existent
          ActiveRecord::Tasks::DatabaseTasks.migrations_paths += Array(engine.paths[migrate_path])
        elsif Dir.exists?(File.join engine.root, migrate_path)
          ActiveRecord::Tasks::DatabaseTasks.migrations_paths << File.join(engine.root, migrate_path)
        end
      end
      Rails.env = ActiveRecord::Tasks::DatabaseTasks.env
      ActiveRecord::Tasks::DatabaseTasks.root = Rails.root if ActiveRecord::Tasks::DatabaseTasks.respond_to? :root

      #puts "env = #{ActiveRecord::Tasks::DatabaseTasks.env}"
      puts "system = #{ENV['SYSTEM']}"
      #puts "config = #{ActiveRecord::Tasks::DatabaseTasks.database_configuration[ActiveRecord::Tasks::DatabaseTasks.env]}"
    end

    def connect
      ActiveRecord::Base.establish_connection ActiveRecord::Tasks::DatabaseTasks.env
    end

    namespace :migrate do
      desc 'Copys the migrations files from the gems directories (options: SYSTEM=(projection|domain))'
      task :copy => :load_config do
        target_path = ActiveRecord::Tasks::DatabaseTasks.migrations_paths.first
        gems = %w(active_event active_domain)
        gems = %w(active_projection) if ENV['SYSTEM'] != 'domain'
        gems.each do |gem|
          gem_spec = Gem::Specification.find_by_name(gem)
          if gem.nil?
            puts "missing gem #{gem}!"
          elsif Dir.exists?(File.join gem_spec.gem_dir, 'db/migrate')
            cp_r (File.join gem_spec.gem_dir, 'db/migrate/.'), target_path
          else
            puts "missing db/migrate in gem #{gem}!"
          end
        end
      end

      desc 'Copy and run migrations (options: SYSTEM=(projection|domain))'
      task :setup do
        Rake::Task[:'disco:db:migrate:copy'].invoke
        Rake::Task[:'disco:db:create'].invoke
        Rake::Task[:'disco:db:migrate'].invoke
      end

      # make sure the right database is connected
      task :down do connect end
      task :up do connect end
      task :status do connect end
    end

    task :migrate do connect end

    namespace :schema do
      task :load do connect end
    end
    namespace :structure do
      task :load do connect end
    end
  end

  load 'active_record/railties/databases.rake'

  namespace :domain do
    task :environment do
      ENV['SYSTEM'] = 'domain'
    end

    namespace :create do
      task :all => [:environment, :'disco:db:create:all']
    end

    desc 'Create the database from config/disco.yml (use create:all to create all dbs in the config)'
    task :create => [:environment, :'disco:db:create']

    namespace :drop do
      task :all => [:environment, :'disco:db:drop:all']
    end

    desc 'Drops the database using config/disco.yml (use drop:all to drop all databases)'
    task :drop => [:environment, :'disco:db:drop']

    desc 'run domain migrations'
    task :migrate => [:environment, :'disco:db:migrate']

    namespace :migrate do
      desc 'Display status of migrations'
      task :status => [:environment, :'disco:db:migrate:status']

      desc 'Copy migrations from rails disco'
      task :copy => [:environment, :'disco:db:migrate:copy']

      desc 'Copy and run migrations from rails disco'
      task :setup => [:environment, :'disco:db:migrate:setup']

      desc 'drop and create, copy and run migrations from rails disco'
      task :reset => [:environment, :'disco:db:migrate:reset']
    end

    desc 'create and load schema and seeds for domain database'
    task :setup => [:environment, :'disco:db:setup']
  end

  def reenable
    Rake::Task.tasks.map &:reenable
  end

  desc 'creates the domain and projection databases for the current environment'
  task :create do
    ENV['SYSTEM'] = 'projection'
    Rake::Task[:'disco:db:create'].invoke

    reenable
    Rake::Task[:'disco:domain:create'].invoke
  end

  desc 'drops the domain and projection databases for the current environment'
  task :drop do
    ENV['SYSTEM'] = 'projection'
    Rake::Task[:'disco:db:drop'].invoke

    reenable
    Rake::Task[:'disco:domain:drop'].invoke
  end

  desc 'migrates the domain and projection databases for the current environment'
  task :migrate do
    ENV['SYSTEM'] = 'projection'
    Rake::Task[:'disco:db:migrate'].invoke

    reenable
    Rake::Task[:'disco:domain:migrate'].invoke
  end

  desc 'Create and load schema and seeds for domain and projection databases'
  task :setup do
    ENV['SYSTEM'] = 'projection'
    Rake::Task[:'disco:db:setup'].invoke

    reenable
    Rake::Task[:'disco:domain:setup'].invoke
  end

  desc 'Drop, recreate and load schema and seeds for domain and projection databases'
  task :reset do
    ENV['SYSTEM'] = 'projection'
    Rake::Task[:'disco:db:drop'].invoke
    Rake::Task[:'disco:db:setup'].invoke

    reenable
    Rake::Task[:'disco:domain:drop'].invoke
    Rake::Task[:'disco:domain:setup'].invoke
  end

  namespace :migrate do
    desc 'copies, creates and runs all migrations for domain and projection databases for the current environment'
    task :setup do
      ENV['SYSTEM'] = 'projection'
      Rake::Task[:'disco:db:migrate:setup'].invoke

      reenable
      Rake::Task[:'disco:domain:migrate:setup'].invoke
    end

    desc 'Resets your domain and projection databases using your migrations for the current environment'
    task :reset do
      ENV['SYSTEM'] = 'projection'
      Rake::Task[:'disco:db:migrate:reset'].invoke

      reenable
      Rake::Task[:'disco:domain:migrate:reset'].invoke
    end

    desc 'Display status of domain and projection migrations'
    task :status do
      ENV['SYSTEM'] = 'projection'
      Rake::Task[:'disco:db:migrate:status'].invoke

      reenable
      Rake::Task[:'disco:domain:migrate:status'].invoke
    end
  end
end
