require "inch/utils/shell_helper"

module Inch
  module API
    # Returns a Compare::Codebases object for two revisions of the same
    # codebase
    class Diff
      include Utils::ShellHelper

      attr_reader :codebase_old
      attr_reader :codebase_new
      attr_reader :comparer
      attr_reader :work_dir

      # @param dir [String] the working directory of the codebase
      # @param before_rev [String] the 'before' revision
      # @param after_rev [String,nil] the 'after' revision that the 'before'
      #   one is compared against
      def initialize(dir, before_rev, after_rev = nil)
        @work_dir = dir
        @codebase_old = codebase_for(before_rev)
        @codebase_new = if after_rev.nil?
                          Codebase.parse(work_dir)
                        else
                          codebase_for(after_rev)
                        end
        @comparer = API::Compare::Codebases.new(@codebase_old, @codebase_new)
      end

      private

      def codebase_for(revision)
        if (cached = codebase_from_cache(revision))
          cached
        else
          codebase = codebase_from_copy(work_dir, revision)
          filename = Codebase::Serializer.filename(revision)
          Codebase::Serializer.save(codebase, filename)
          codebase
        end
      end

      def codebase_from_cache(revision)
        filename = Codebase::Serializer.filename(revision)
        Codebase::Serializer.load(filename) if File.exist?(filename)
      end

      def codebase_from_copy(original_dir, revision)
        codebase = nil
        Dir.mktmpdir do |tmp_dir|
          new_dir = copy_work_dir(original_dir, tmp_dir)
          git_reset(new_dir, revision)
          codebase = Codebase.parse(new_dir)
        end
        codebase
      end

      def copy_work_dir(original_dir, tmp_dir)
        git tmp_dir, "clone #{original_dir} --quiet"
        File.join(tmp_dir, File.basename(original_dir))
      end

      def git_reset(dir, revision = nil)
        git dir, "reset --hard #{revision}"
      end
    end
  end
end