require_relative 'base/underlying'
require_relative 'base/subrepo'

module EacLauncher
  module Git
    class Base < ::EacLauncher::Path
      include ::Eac::SimpleCache
      include ::EacLauncher::Git::Base::Subrepo
      include ::EacLauncher::Git::Base::Underlying

      def init_bare
        FileUtils.mkdir_p(self)
        ::EacRubyUtils::Envs.local.command('git', 'init', '--bare', self).execute! unless
        File.exist?(subpath('.git'))
      end

      def rev_parse(ref)
        r = execute!('rev-parse', ref, exit_outputs: { 128 => nil, 32_768 => nil })
        r.strip! if r.is_a?(String)
        r = nil if r == ''
        r
      end

      def remote_hashs(remote_name)
        r = {}
        execute!(['ls-remote', remote_name]).each_line do |line|
          x = line.strip.split(/\s+/)
          r[x[1]] = x[0]
        end
        r
      end

      def remote_exist?(remote_name)
        git.remote(remote_name).url.present?
      end

      def descendant?(descendant, ancestor)
        base = merge_base(descendant, ancestor)
        return false if base.blank?
        revparse = execute!('rev-parse', '--verify', ancestor).strip
        base == revparse
      end

      def merge_base(*commits)
        refs = commits.dup
        while refs.count > 1
          refs[1] = merge_base_pair(refs[0], refs[1])
          return nil if refs[1].blank?
          refs.shift
        end
        refs.first
      end

      def subtree_split(prefix)
        execute!('subtree', '-q', 'split', '-P', prefix).strip
      end

      def assert_remote_url(remote_name, url)
        r = git.remote(remote_name)
        if !r.url || r.url != url
          r.remove if r.url
          git.add_remote(remote_name, url)
        end
        r
      end

      def remote_branch_sha(remote_name, branch_name)
        remote_hashs(remote_name)["refs/heads/#{branch_name}"]
      end

      def push(remote_name, refspecs, options = {})
        refspecs = [refspecs] unless refspecs.is_a?(Array)
        args = ['push']
        args << '--dry-run' if options[:dryrun]
        args << '--force' if options[:force]
        system!(args + [remote_name] + refspecs)
      end

      def push_all(remote_name)
        system!('push', '--all', remote_name)
        system!('push', '--tags', remote_name)
      end

      def fetch(remote_name, options = {})
        args = ['fetch', '-p', remote_name]
        args << '--tags' if options[:tags]
        execute!(*args)
      end

      def current_branch
        execute!(%w[symbolic-ref -q HEAD]).gsub(%r{\Arefs/heads/}, '').strip
      end

      def reset_hard(ref)
        execute!('reset', '--hard', ref)
      end

      private

      def merge_base_pair(c1, c2)
        execute!('merge-base', c1, c2, exit_outputs: { 256 => '' }).strip
      end
    end
  end
end