# frozen_string_literal: true

require 'git/commands/base'

module Git
  module Commands
    # Implements the `git clone` command
    #
    # Clones a repository into a newly created directory.
    #
    # @example Typical usage
    #   clone = Git::Commands::Clone.new(execution_context)
    #   clone.call('https://github.com/user/repo.git')
    #   clone.call('https://github.com/user/repo.git', 'local-dir')
    #   clone.call('https://github.com/user/repo.git', 'local-dir', bare: true)
    #   clone.call('https://github.com/user/repo.git', 'local-dir', depth: 1)
    #
    # @note `arguments` block audited against https://git-scm.com/docs/git-clone/2.53.0
    #
    # @see https://git-scm.com/docs/git-clone git-clone
    #
    # @see Git::Commands
    #
    # @api private
    #
    class Clone < Git::Commands::Base
      arguments do
        literal 'clone'
        value_option :template, inline: true
        flag_option %i[local l], negatable: true
        flag_option %i[shared s]
        flag_option :no_hardlinks
        flag_option %i[quiet q]
        flag_option %i[verbose v]
        flag_option :progress
        flag_option %i[no_checkout n]
        flag_option :bare
        flag_option :mirror
        value_option %i[origin o]
        value_option %i[branch b]
        value_option :revision, inline: true
        value_option %i[upload_pack u]
        value_option :reference, repeatable: true
        value_option :reference_if_able, repeatable: true
        flag_option :dissociate
        value_option :separate_git_dir, inline: true
        value_option :server_option, inline: true, repeatable: true
        value_option :depth
        value_option :shallow_since, inline: true
        value_option :shallow_exclude, inline: true, repeatable: true
        flag_option :single_branch, negatable: true
        flag_option :tags, negatable: true
        flag_or_value_option :recurse_submodules, inline: true, repeatable: true
        flag_option :shallow_submodules, negatable: true
        flag_option :remote_submodules, negatable: true
        value_option %i[jobs j]
        flag_option :sparse
        flag_option :reject_shallow, negatable: true
        value_option :filter, inline: true
        flag_option :also_filter_submodules
        value_option %i[config c], repeatable: true
        value_option :bundle_uri, inline: true
        value_option :ref_format, inline: true
        end_of_options
        operand :repository, required: true
        operand :directory
        execution_option :timeout
        execution_option :chdir
      end

      # @overload call(repository, directory = nil, **options)
      #
      #     Execute the `git clone` command
      #
      #     @param repository [String] the URL or path of the repository to clone
      #
      #     @param directory [String, nil] the directory to clone into; git derives the
      #       name from the repository URL when omitted
      #
      #     @param options [Hash] command options
      #
      #     @option options [String] :template (nil) directory from which templates
      #       will be used
      #
      #     @option options [Boolean, nil] :local (nil) bypass the normal Git-aware transport
      #       for local clones (`--local`)
      #
      #       Alias: `:l`
      #
      #     @option options [Boolean, nil] :no_local (nil) disable the local optimization
      #       and use the normal transport (`--no-local`)
      #
      #     @option options [Boolean, nil] :shared (nil) set up a shared clone using
      #       alternates instead of hardlinks
      #
      #       Alias: `:s`
      #
      #     @option options [Boolean, nil] :no_hardlinks (nil) force file-copy instead of
      #       hardlinks when cloning from a local filesystem
      #
      #     @option options [Boolean, nil] :quiet (nil) suppress progress output to stderr
      #
      #       Alias: `:q`
      #
      #     @option options [Boolean, nil] :verbose (nil) run verbosely
      #
      #       Alias: `:v`
      #
      #     @option options [Boolean, nil] :progress (nil) force progress status even when
      #       stderr is not a terminal
      #
      #     @option options [Boolean, nil] :no_checkout (nil) skip checking out HEAD after
      #       the clone
      #
      #       Alias: `:n`
      #
      #     @option options [Boolean, nil] :bare (nil) clone as a bare repository
      #
      #     @option options [Boolean, nil] :mirror (nil) set up a mirror clone (implies
      #       --bare)
      #
      #     @option options [String] :origin (nil) name of the remote to use instead of
      #       "origin"
      #
      #       Alias: `:o`
      #
      #     @option options [String] :branch (nil) branch to check out after cloning
      #
      #       Alias: `:b`
      #
      #     @option options [String] :revision (nil) detach HEAD at the given revision
      #       after cloning; incompatible with :branch and :mirror
      #
      #     @option options [String] :upload_pack (nil) path to git-upload-pack on the
      #       remote (ssh only)
      #
      #       Alias: `:u`
      #
      #     @option options [String, Array<String>] :reference (nil) borrow objects from
      #       one or more reference repositories
      #
      #     @option options [String, Array<String>] :reference_if_able (nil) like
      #       :reference but skip with a warning when the reference does not exist
      #
      #     @option options [Boolean, nil] :dissociate (nil) stop borrowing from reference
      #       repositories after the clone is complete
      #
      #     @option options [String] :separate_git_dir (nil) place the git directory at
      #       the given path and create a gitfile symlink in the working tree
      #
      #     @option options [String, Array<String>] :server_option (nil) protocol-v2
      #       server options
      #
      #     @option options [Integer, String] :depth (nil) create a shallow clone with
      #       the specified number of commits
      #
      #     @option options [String] :shallow_since (nil) create a shallow clone with
      #       history after the specified date
      #
      #     @option options [String, Array<String>] :shallow_exclude (nil) exclude
      #       commits reachable from the specified remote branch or tag
      #
      #     @option options [Boolean, nil] :single_branch (nil) clone only the history for
      #       one branch (`--single-branch`)
      #
      #     @option options [Boolean, nil] :no_single_branch (nil) clone history for all
      #       branches (`--no-single-branch`)
      #
      #     @option options [Boolean, nil] :tags (nil) include tags in the clone (`--tags`)
      #
      #     @option options [Boolean, nil] :no_tags (nil) exclude tags from the clone
      #       (`--no-tags`)
      #
      #     @option options [Boolean, String, Array<String>, nil] :recurse_submodules (nil)
      #       initialize submodules after cloning; pass true for all submodules or a
      #       pathspec string/array for a subset
      #
      #     @option options [Boolean, nil] :shallow_submodules (nil) clone submodules with
      #       depth 1 (`--shallow-submodules`)
      #
      #     @option options [Boolean, nil] :no_shallow_submodules (nil) clone submodules
      #       with full history (`--no-shallow-submodules`)
      #
      #     @option options [Boolean, nil] :remote_submodules (nil) use submodule
      #       remote-tracking branch status (`--remote-submodules`)
      #
      #     @option options [Boolean, nil] :no_remote_submodules (nil) use the locally
      #       recorded SHA-1 for submodules (`--no-remote-submodules`)
      #
      #     @option options [Integer, String] :jobs (nil) number of submodules fetched
      #       concurrently
      #
      #       Alias: `:j`
      #
      #     @option options [Boolean, nil] :sparse (nil) enable sparse checkout with
      #       top-level files only
      #
      #     @option options [Boolean, nil] :reject_shallow (nil) fail if source is shallow
      #       (`--reject-shallow`)
      #
      #     @option options [Boolean, nil] :no_reject_shallow (nil) allow cloning from
      #       shallow sources (`--no-reject-shallow`)
      #
      #     @option options [String] :filter (nil) specify a partial clone filter
      #       (e.g., 'blob:none', 'tree:0')
      #
      #     @option options [Boolean, nil] :also_filter_submodules (nil) apply the partial
      #       clone filter to submodules; requires :filter and :recurse_submodules
      #
      #     @option options [String, Array<String>] :config (nil) set configuration
      #       variables in the newly-created repository
      #
      #       Alias: `:c`
      #
      #     @option options [String] :bundle_uri (nil) fetch a bundle from the given URI
      #       before fetching from the remote
      #
      #     @option options [String] :ref_format (nil) specify the ref storage format
      #       (e.g., 'files', 'reftable')
      #
      #     @option options [Numeric, nil] :timeout (nil) the number of seconds to wait
      #       for the command to complete; if nil, uses the global timeout from
      #       {Git::Config}; if 0, no timeout is enforced
      #
      #     @option options [String, nil] :chdir (nil) the working directory in which to
      #       run the git clone command
      #
      #     @return [Git::CommandLine::Result] the result of calling `git clone`
      #
      #     @raise [ArgumentError] if unsupported options are provided
      #
      #     @raise [Git::FailedError] if git exits with a non-zero exit status
      #
      #   @api public
      #
      def call(*, **)
        super
      end
    end
  end
end
