class Storey::Duplicator
  attr_accessor :source_schema, :target_schema, :source_file, :target_file, :structure_only, :file_prefix, :dump_path, :source_dump_path, :target_dump_path

  def initialize(from_schema, to_schema, options={})
    fail Storey::SchemaNotFound, "cannot duplicate from nil schema" unless from_schema

    self.dump_path = File.join Rails.root, 'tmp', 'schema_dumps'
    self.source_dump_path = File.join self.dump_path, 'source'
    self.target_dump_path = File.join self.dump_path, 'target'
    self.structure_only = options[:structure_only] || false

    self.source_schema = Storey.suffixify from_schema
    self.target_schema = Storey.suffixify to_schema
    self.file_prefix = "#{Time.now.to_i}_#{rand(100000)}"
    self.source_file   = File.join self.source_dump_path, "#{self.file_prefix}_#{self.source_schema}.sql"
    self.target_file   = File.join self.target_dump_path, "#{self.file_prefix}_#{self.target_schema}.sql"
  end

  def perform!
    dump_schema
    replace_occurances
    load_schema
  end

  private

  def dump_schema(options={})
    ENV['PGPASSWORD'] = Storey.database_config[:password]
    prepare_schema_dump_directories

    options[:host]     ||= Storey.database_config[:host] unless Storey.database_config[:host].blank?
    options[:username] ||= Storey.database_config[:username]
    options[:file]     ||= self.source_file
    options[:schema]   ||= self.source_schema

    switches = options.map { |k, v| "--#{k}=#{v}" }
    switches << '--schema-only' if self.structure_only
    switches = switches.join(" ")

    `pg_dump #{switches} #{Storey.database_config[:database]}`
  end

  def prepare_schema_dump_directories
    [self.source_dump_path, self.target_dump_path].each { |d| FileUtils.mkdir_p(d) }
  end

  def load_schema(options={})
    options[:file] ||= self.target_file
    switches = Storey.command_line_switches(options)

    if duplicating_from_default?
      # Since we are copying the source schema and we're after structure only,
      # the dump_schema ended up creating a SQL file without the "CREATE SCHEMA" command
      # thus we have to create it manually
      ::Storey.create_plain_schema self.target_schema
    end

    `psql #{switches}`

    copy_source_schema_migrations

    ENV['PGPASSWORD'] = nil
  end

  def copy_source_schema_migrations
    ::Storey.switch self.target_schema do
      source_schema_migrations.each do |version|
        unless target_schema_migrations.include?(version)
          ActiveRecord::Base.connection.execute "INSERT INTO schema_migrations (version) VALUES ('#{version}');"
        end
      end
    end
  end

  def source_schema_migrations
    ::Storey.switch(self.source_schema) do
      ActiveRecord::Migrator.get_all_versions
    end
  end

  def target_schema_migrations
    ::Storey.switch(self.target_schema) do
      ActiveRecord::Migrator.get_all_versions
    end
  end

  def replace_occurances
    File.open(self.source_file, 'r') do |file|
      file.each_line do |line|
        new_line = line.gsub(/#{self.source_schema}/, self.target_schema)
        File.open(self.target_file, 'a') {|tf| tf.puts new_line}
      end
    end
  end

  def duplicating_from_default?
    ::Storey.matches_default_search_path?(self.source_schema) && self.structure_only
  end
end