#!/usr/bin/env ruby.ruby2.5
require File.dirname(__FILE__) + '/../config/boot'

# set this to true if you want to create users that appear in metafiles
# (useful for importing on devel workstations)
#
# if false, an exception is thrown instead
CREATE_MISSING_USERS = true

# all users created in the above mentioned way will have those attributes
DUMMY_USER_ATTR = { password: 'asdfasdf', email: 'noone@localhost' }.freeze

require 'activexml/activexml'

### boot rails ###
Rails::Initializer.run do |config|
  config.frameworks -= [:action_web_service, :active_resource]

  config.action_controller.session = {
    session_key: '_frontend_session',
    secret:      'ad9712p8349zqmowiefzhiuzgfp9s8f7qp83947p98weap9866e7'
  }
end

ActiveXML::Node.config do |conf|
  conf.lazy_evaluation = true

  conf.setup_transport do |map|
    map.default_server :rest, "#{CONFIG['source_protocol']}://#{CONFIG['source_host']}:#{CONFIG['source_port']}"

    map.connect :xproject, 'rest:///source/:name/_meta',
                all: 'rest:///source/:name'
    map.connect :xpackage, 'rest:///source/:project/:name/_meta',
                all: 'rest:///source/:project/:name/'
  end
end

class Xproject < ActiveXML::Node
end

class Xpackage < ActiveXML::Node
end

def store_project(p)
  STDERR.print "  #{p.name} => "
  begin
    create_missing_users_for p

    p.save
    STDERR.puts 'success'
    store_packages_for p
    return true
  rescue Project::SaveError => e
    if e.message =~ /^unable to walk on path/
      STDERR.puts 'delayed'
      return false
    else
      STDERR.puts 'FAILED'
      raise e
    end
  end
end

def store_packages_for(p)
  STDERR.puts "  --> storing packages for project '#{p.name}'"
  list = Xpackage.find :all, project: p.name
  xpack = nil
  list.each_entry do |entry|
    STDERR.print "     #{entry.name} => "
    xpack = Xpackage.find entry.name, project: p.name
    # remove devel entries
    xpack.data.each_element('devel') { |e| xpack.data.delete_element e }
    pack = Package.new(xpack.dump_xml, project: p.name)

    create_missing_users_for xpack

    pack.save
    STDERR.puts 'success'
  end
  STDERR.puts '  <-- fin'
rescue Object => e
  # remove whole project to cause missing packages to be read at next run
  STDERR.puts '### store_packages exception handling'
  Project.find_by_name(p.name.to_s).destroy
  xmldebug xpack.dump_xml
  raise e
end

@xmldebug_called = false
def xmldebug(str)
  STDERR.puts caller
  return if @xmldebug_called
  @xmldebug_called = true
  STDERR.puts "\n\n>>> Exception caused by this xml:\n" + str + "\n\n"
end

def create_missing_users_for(meta)
  return unless CREATE_MISSING_USERS
  meta.each_person do |person|
    u = User.find_by_login person.userid.to_s
    next if u
    attribs = { login: person.userid.to_s }.merge DUMMY_USER_ATTR
    u = User.create attribs
    u.save
  end
end

filter = []
# filter = ["Fedora:Core6", "Fedory:Extras6"];
# filter = ['Apache:Modules', 'Apache', 'Fedora:Core5', 'Mandriva:2006', 'openSUSE:10.2', 'SUSE:SLES-9', 'SUSE:SLE-10:SDK',
#  'Java:Sun-Java-1.5', 'SUSE:SL-10.1', 'SUSE:SL-10.0', 'SUSE:SL-9.3', 'SUSE:SLE-10', 'openSUSE:Factory'
# ];
# filter = %w(devel:tools:building SUSE:SLE-10:SP2:SDK home:dirkmueller:playground:kde4)

projects = Xproject.find(:all)

# maps project name to an array: [project_object, {packagename => package_object, ...}]
cache = {}

STDERR.puts '--> storing projects'
projects.each_entry do |entry|
  # if filter is set, only fetch the projects in the array
  unless filter.empty?
    next unless filter.include? entry.name
  end

  p = nil
  begin
    p = Project.find(entry.name)
  rescue ActiveXML::Transport::NotFoundError
  end
  next if p

  backend_project = Xproject.find entry.name
  p = Project.new(backend_project.dump_xml)

  cache[entry.name] = p unless store_project(p)
end

run_count = 0

until cache.empty?
  STDERR.puts "--> trying to store previously unsaved projects, run [#{run_count += 1}]"
  tmp = cache.values.clone
  tmp.each do |p|
    p.each_repository do |repo|
      repo.each_path do |path|
        unless cache.key?(path.project.to_s) || Project.find_by_name(path.project.to_s)
          STDERR.puts "fetching dependency #{path.project}"
          cache[path.project.to_s] = Project.new(Xproject.find(path.project.to_s).dump_xml)
        end
      end
    end

    begin
      if store_project(p)
        cache.delete p.name.to_s
        STDERR.puts "*** cache.length: #{cache.length}"
      end
    rescue ActiveRecord::StatementInvalid => e
      raise e
    end
  end
end
