#!/usr/bin/env ruby -KU

ENV["PATH"] = "/usr/local/bin/:/opt/local/bin:#{ENV["PATH"]}"
ENV["RAILS_ENV"] ||= "production"

require File.dirname(__FILE__) + "/../config/environment"

abort("Usage: #{$0} repo_id") unless ARGV[0]

$stdout.sync = true

def create_repo_creation_events_for(project)
  project.repository_clones.each do |repo|
    puts "creating Repository clone event in #{project.slug}/#{repo.name}"
   project.events.create({
     :action => Action::CLONE_REPOSITORY,
     :target => repo,
     :user => repo.user,
     :data => repo.parent_id,
     :created_at => repo.created_at
   })
  end
end

def create_comment_events_for(repo, project)
  repo.comments.each do |comment| 
    puts "creating Comment event on #{project.slug}/#{repo.name}"
    project.events.create({
      :action => Action::COMMENT,
      :target => comment,
      :user => comment.user,
      :created_at => comment.created_at
    })
  end
end

def create_merge_request_events_for(repo, project)
  repo.merge_requests.each do |mr|
    puts "creating MergeRequest event on #{project.slug}/#{repo.name}"
    project.events.create({
      :action => Action::REQUEST_MERGE,
      :target => mr,
      :user => mr.user,
      :created_at => mr.created_at,
    })
    unless mr.open?
      puts "creating MergeRequest resolvement event on #{project.slug}/#{repo.name}"
      project.events.create({
        :action => Action::RESOLVE_MERGE_REQUEST,
        :target => mr,
        :user => mr.user,
        :data => mr.status_string,
        :created_at => mr.created_at,
      })
    end
  end
end

def create_events_for_repository(repo, project)
  tag_map = repo.git.tags.inject({}){|hsh, t| hsh[t.commit.id] = t.name;hsh}

  parsed_commits = {} # Neccesary because of merge
  repo.git.heads.each do |head|
    users_commits = {}
  
    Grit::Commit.find_all(repo.git, head.name, {:since => "1 year ago"}).each do |commit|
      users_commits[commit.committer.email] ||= []
      users_commits[commit.committer.email] << commit
    end
    
    users = User.find(:all, :conditions => ['email in (?)', users_commits.keys] )
    users.each do |user|
      commits = users_commits[user.email]
      puts "\nindexing #{commits.size} commits for #{user.email} in #{project.slug}/#{repo.name}:#{head.name}"
      commits.each_index do |i|
        commit = commits[i]
        previous_commit = commits[i-1]
      
        newrev = commit.id
        next if parsed_commits.has_key?(newrev)
        parsed_commits[newrev] = true
      
        oldrev = previous_commit.id if previous_commit
        current_rev = newrev
        newtype = oldtype = current_rev_type = "commit"
      
        type = repo.git.git.name_rev({}, newrev).last.split("/").first
        if type != "tags"
          type = "heads"
        end
      
        action = :create
        if !oldrev
          action = :create
        else
          if commit.id =~ /^0+$/
            action = :delete
          else
            action = :update
          end
        end

        if action != :delete
          newtype = repo.git.git.cat_file({:t => true}, newrev)
        end

        if action == :update
          oldtype = repo.git.git.cat_file({:t => true}, oldrev)
        end

        if action == :delete
            current_rev = oldrev
            current_rev_type = oldtype
        end
      

      
        action_id = nil
        ref = nil
      
        if current_rev_type == "commit"
          if type == "heads"
            case action
              when :create
                action_id = Action::CREATE_BRANCH
                ref = head.name
              when :update
                action_id = Action::COMMIT
                ref = current_rev
              when :delete
                action_id = Action::DELETE_BRANCH
                ref = head.name
            end
          elsif type == "tags"
            if action == :create
              action_id = Action::CREATE_TAG
              ref = tag_map[commit.id]
            elsif action == :delete
              action_id = Action::DELETE_TAG
              ref = tag_map[commit.id]
            end
          end
        elsif current_rev_type == "tag"
          if type == "tags"
            if action == :create
              action_id = Action::CREATE_TAG
              ref = tag_map[commit.id]
            elsif action == :delete
              action_id = Action::DELETE_TAG
              ref = tag_map[commit.id]
            end
          end
        end
      
        print "." if (i % 10 == 1)
        #puts "#{current_rev_type}#{action.inspect} in #{project.slug}/#{repo.name}:#{head.name}: #{commit.short_message}"
        project.events.create({
          :action => action_id || Action::COMMIT, 
          :target => repo, 
          :user => user, 
          :body => commit.message, 
          :data => commit.id,
          :created_at => commit.committed_date})
      end
      commits = nil
      puts
    end
    users_commits = nil
  end
  parsed_commits = nil
end

def rebuild_project!(project)
  puts "Destroying existing events on #{project.slug}"
  project.events.destroy_all

  project.repositories.each do |repo|
    create_events_for_repository(repo, project)
    create_comment_events_for(repo, project)
    create_merge_request_events_for(repo, project)
  end
  create_repo_creation_events_for(project)
end

case ARGV[0]
when "all"
  Project.find(:all).each do |project|
    puts 
    puts "rebuilding #{project.slug}"
    puts
    begin
      rebuild_project!(project)
      GC.start
    rescue
      puts "!!! failed to rebuild #{project.slug} !!!"
      puts "#{e.class}:#{e.message} \n#{e.backtrace.join("\n  ")}"
      puts
      next
    end
  end
else
  project = Project.find(ARGV[0])
  rebuild_project!(project) if project
end

