require “logger” require “uri” require “pliny/utils”

begin

require "sequel"
require "sequel/extensions/migration"
require "pliny/db_support"

namespace :db do
  desc "Get current schema version"
  task :version do
    Pliny::DbSupport.run(database_urls.first, $stdout) do |helper|
      puts "Current version: #{helper.version}"
    end
  end

  namespace :migrate do
    desc "Get the status of migrations"
    task :status do
      # TODO: figure out how to handle multiple databases. Also why do we support multiple databases this way?
      Pliny::DbSupport.run(database_urls.first, $stdout) do |helper|
        puts helper.status
      end
    end
  end

  desc "Run database migrations"
  task :migrate do
    next if Dir["./db/migrate/*.rb"].empty?
    database_urls.each do |database_url|
      Pliny::DbSupport.run(database_url, $stdout) do |helper|
        helper.migrate
        puts "Migrated `#{name_from_uri(database_url)}`"
      end
    end
  end

  desc "Rollback last database migration"
  task :rollback do
    next if Dir["./db/migrate/*.rb"].empty?
    database_urls.each do |database_url|
      Pliny::DbSupport.run(database_url, $stdout) do |helper|
        helper.rollback
        puts "Rolled back `#{name_from_uri(database_url)}`"
      end
    end
  end

  desc "Seed the database with data"
  task :seed do
    if File.exist?("./db/seeds.rb")
      database_urls.each do |database_url|
        # make a DB instance available to the seeds file
        self.class.send(:remove_const, "DB") if self.class.const_defined?("DB")
        self.class.const_set("DB", Sequel.connect(database_url))
        load "db/seeds.rb"
      end
      disconnect
    end
  end

  desc "Create the database"
  task :create do
    database_urls.each do |database_url|
      db_name = name_from_uri(database_url)
      if Pliny::DbSupport.setup?(database_url)
        puts "Skipping `#{db_name}`, already exists"
      else
        admin_url = Pliny::DbSupport.admin_url(database_url)
        Pliny::DbSupport.run(admin_url) do |helper|
          helper.create(db_name)
          puts "Created `#{db_name}`"
        end
      end
    end
    disconnect
  end

  desc "Drop the database"
  task :drop do
    database_urls.each do |database_url|
      admin_url = Pliny::DbSupport.admin_url(database_url)
      db = Sequel.connect(admin_url)
      name = name_from_uri(database_url)
      db.run(%(DROP DATABASE IF EXISTS "#{name}"))
      puts "Dropped `#{name}`"
    end
    disconnect
  end

  namespace :schema do
    desc "Load the database schema"
    task :load do
      if File.exist?("./db/schema.sql")
        schema = File.read("./db/schema.sql")
        database_urls.each do |database_url|
          db = Sequel.connect(database_url)
          db.run(schema)
          puts "Loaded `#{name_from_uri(database_url)}`"
        end
        disconnect
      else
        puts "Skipped schema load, schema.sql not present"
      end
    end

    desc "Dump the database schema"
    task :dump do
      file = File.join("db", "schema.sql")
      database_url = database_urls.first
      `pg_dump -s -x -O -f #{file} #{database_url}`

      schema = File.read(file)
      # filter all COMMENT ON EXTENSION, only owners and the db
      # superuser can execute these, making it impossible to just
      # replay such statements in certain production databases
      schema.gsub!(/^COMMENT ON EXTENSION.*\n/, "")

      # add migrations used to compose this schema
      db = Sequel.connect(database_urls.first)
      if db.table_exists?(:schema_migrations)
        # set the search_path so migrations are added in the right schema
        search_path = db.dataset.with_sql("SHOW search_path").single_value
        schema << "SET search_path = #{search_path};\n\n"

        db[:schema_migrations].order_by(:filename).each do |migration|
          schema << db[:schema_migrations].insert_sql(migration) + ";\n"
        end
      end
      disconnect

      File.open(file, "w") { |f| f.puts schema }
      puts "Dumped `#{name_from_uri(database_url)}` to #{file}"
    end

    desc "Merges migrations into schema and removes them"
    task merge: ["db:setup", "db:schema:dump"] do
      FileUtils.rm Dir["./db/migrate/*.rb"]
      puts "Removed migrations"
    end
  end

  desc "Setup the database"
  task setup: [:drop, :create, "schema:load", :migrate, :seed]

  private

  def disconnect
    Sequel::DATABASES.each { |db| db.disconnect }
    Sequel::DATABASES.clear
  end

  def database_urls
    if ENV["DATABASE_URL"]
      [ENV["DATABASE_URL"]]
    else
      %w[.env .env.test].map { |env_file|
        env_path = "./#{env_file}"
        if File.exist?(env_path)
          Pliny::Utils.parse_env(env_path)["DATABASE_URL"]
        end
      }.compact
    end
  end

  def name_from_uri(uri)
    URI.parse(uri).path[1..-1]
  end
end

rescue LoadError

puts "Couldn't load sequel. Skipping database tasks"

end