namespace :db do
  namespace :data do
    namespace :safe do
      desc "Exports a JSON file containing data that should be considered safe and non critical"
      task :dump => :environment do
        patch_serialization
        extra_accounts = [Account.find_by_domain("demosite.3scale.net")]
        File.open("#{Rails.root}/db/safe.json","w") do |f|
          f.puts account_json_output(Account.master)
          f.puts Account.master.bought_cinstances.to_json
          extra_accounts.each do |a|
            f.puts account_json_output(a)
            f.puts [a.bought_cinstances, a.provided_cinstances].flatten.uniq.to_json
            a.buyer_accounts.each do |child|
              f.puts account_json_output(child)
            end
          end
          f.puts Country.find(:all).to_json
        end
      end

      desc "Load data from db/safe.json into current database, erasing any previous content"
      task :load => :environment do
        confirm_production_kill
        Rake::Task['db:drop'].execute({})
        Rake::Task['db:create'].execute({})
        Rake::Task['db:schema:load'].execute({})
        ActiveSupport::JSON.backend = 'JSONGem'

        dump = ENV['SOURCE'] || "#{Rails.root}/db/safe.json"

        unless File.exist?(dump)
          abort 'No safe dump found. Run "cap db:safe_export" first.'
        end

        config = ActiveRecord::Base.configurations[Rails.env]

        puts "Loading data from #{dump} into #{config['database']} database..."
        ActiveRecord::Base.connection.disable_referential_integrity do
          File.open("#{Rails.root}/db/safe.json", "r") do |f|
            f.each_line do |line|
              decoded_json = ActiveSupport::JSON.decode(line)
              if decoded_json.is_a? Hash
                insert_object(decoded_json.values.first, decoded_json.keys.first.camelize.constantize.table_name)
              else #array
                decoded_json.each {|i| insert_object(i.values.first, i.keys.first.camelize.constantize.table_name)}
              end
            end
          end
        end
        puts "Done."
      end

      def insert_object(obj_hash, table_name, extra_param={})
        exceptions = {"versions" => "liquid_page_versions"}

        columns_to_insert = {}.merge(extra_param)
        obj_hash.keys.each do |k|
          if k.ends_with? "attributes"
            inner_table_name = exceptions[k.gsub('_attributes','')] || k.gsub('_attributes','')
            if obj_hash[k].is_a?(Array) # collection
              obj_hash[k].each do |e|
                asoc_key = begin
                  table_name.camelize.singularize.constantize.reflect_on_association(inner_table_name.to_sym).primary_key_name.to_s
                rescue
                  "#{table_name.singularize}_id"
                end
                insert_object(e, inner_table_name, asoc_key => obj_hash["id"])
              end
            else # has_one association
              insert_object(obj_hash[k], inner_table_name,"#{table_name.singularize}_id" => obj_hash["id"])
            end
          else
            columns_to_insert[k] = obj_hash[k]
          end
        end
        values = columns_to_insert.values.map{|v| ActiveRecord::Base.connection.quote v}
        ActiveRecord::Base.connection.disable_referential_integrity do
          print "."
          ActiveRecord::Base.connection.execute "INSERT INTO #{table_name} (#{columns_to_insert.keys.join(',')}) VALUES (#{values.join(',')});"
        end
      end

      def account_json_output(acct)
        acct.to_json(:except => [:providerbalance, :buyerbalance] ,:include => {:wiki_pages => {:include => {:slugs => {}}}, :mail_dispatch_rules => {}, :settings => {}, :liquid_pages => {:include => {:theme => {}, :versions => {}}}, :users => {:include => {:search_terms => {}, :monitorships => {}}}, :forums => {:include => {:topics => {}, :posts => {}, :moderatorships => {}}}, :assets => {}, :app_exhibits => {}, :services => {:include => {:features => {}, :metrics => {}, :plans => {:include => {:pricing_rules => {}, :usage_limits => {}}}}}})
      end

      def patch_serialization
        ActiveRecord::Serialization::Serializer.module_eval do
          def serializable_record
            returning(serializable_record = {}) do
              serializable_names.each { |name| serializable_record[name] = @record.send(name) }
              add_includes do |association, records, opts|
                serializable_record["#{association}_attributes"] = if records.is_a?(Enumerable)
                  records.collect { |r| self.class.new(r, opts).serializable_record }
                                                                   else
                  self.class.new(records, opts).serializable_record
                                                                   end
              end
            end
          end
        end
        Account.module_eval do
          def wiki_pages; wiki_pages_without_lazy_home_creation; end
        end
      end

      def confirm_production_kill
        if Rails.env.production? && ENV['FORCE'].blank?
          abort "Are you sure you want to kill your PRODUCTION DATABASE? " +
                "If yes, run this task with parameter FORCE=true."
        end
      end
    end
  end
end
