# frozen_string_literal: true

require 'tempfile'
require 'English'
require 'puppetfile-resolver/util'
require 'puppetfile-resolver/spec_searchers/common'
require 'puppetfile-resolver/spec_searchers/git_configuration'
require 'uri'

module PuppetfileResolver
  module SpecSearchers
    module Git
      module GClone
        METADATA_FILE = 'metadata.json'

        # @summary clones the remote url and reads the metadata file
        # @returns [String] the content of the metadata file
        def self.metadata(puppetfile_module, resolver_ui, config)
          repo_url = puppetfile_module.remote

          unless PuppetfileResolver::Util.git?
            resolver_ui.debug { 'Git executible not found, unable to use git clone resolution' }

            return nil
          end
          return nil if repo_url.nil?
          return nil unless valid_http_url?(repo_url)

          ref = puppetfile_module.ref ||
                puppetfile_module.tag ||
                puppetfile_module.commit ||
                puppetfile_module.branch ||
                'HEAD'

          resolver_ui.debug { "Querying git repository #{repo_url}" }

          clone_and_read_file(repo_url, ref, config)
        end

        # @summary clones the git url and reads the file at the given ref
        #          a temp directory will be created and then destroyed during
        #          the cloning and reading process
        # @param ref [String] the git ref, branch, commit, tag
        # @param file [String] the file you wish to read
        # @returns [String] the content of the file
        def self.clone_and_read_file(url, ref, config)
          Dir.mktmpdir(nil, config.clone_dir) do |dir|
            clone = ['git', 'clone', url, dir]
            clone += ['--config', "http.proxy=#{config.proxy}", '--config', "https.proxy=#{config.proxy}"] if config.proxy

            bare_clone = clone + ['--bare', '--depth=1']
            bare_clone.push("--branch=#{ref}") unless ref == 'HEAD'

            # Try to clone a bare repository. If that fails, fall back to a full clone.
            # Cloning might fail because the repo does not exist or is otherwise
            # inaccessible, but it can also fail because cloning a bare repository from
            # a commit/SHA1 fails. Falling back to a full clone ensures that we support
            # commits/SHA1s like Puppetfile does.
            _stdout, _stderr, process = ::PuppetfileResolver::Util.run_command(bare_clone)

            unless process.success?
              _stdout, stderr, process = ::PuppetfileResolver::Util.run_command(clone)

              unless process.success?
                msg = if config.proxy
                        "Cloning #{url} with proxy #{config.proxy} failed: #{stderr}"
                      else
                        "Cloning #{url} failed: #{stderr}"
                      end
                raise msg
              end
            end

            Dir.chdir(dir) do
              content, stderr, process = ::PuppetfileResolver::Util.run_command(['git', 'show', "#{ref}:#{METADATA_FILE}"])
              raise "Could not find #{METADATA_FILE} for ref #{ref} at #{url}: #{stderr}" unless process.success?
              return content
            end
          end
        end

        def self.valid_http_url?(url)
          # uri does not work with git urls, return true
          return true if url.start_with?('git@')

          uri = URI.parse(url)
          uri.is_a?(URI::HTTP) && !uri.host.nil?
        rescue URI::InvalidURIError
          false
        end
      end
    end
  end
end
