require "pact_broker/client/hash_refinements"

module PactBroker
  module Client
    module CLI
      # Thor::Error will have its backtrace hidden
      class PactPublicationError < ::Thor::Error; end

      module PactCommands
        using PactBroker::Client::HashRefinements

        def self.included(thor)
          thor.class_eval do
            desc 'publish PACT_DIRS_OR_FILES ...', "Publish pacts to a Pact Broker."
            method_option :consumer_app_version, required: true, aliases: "-a", desc: "The consumer application version"
            method_option :branch, aliases: "-h", desc: "Repository branch of the consumer version"
            method_option :auto_detect_version_properties, hidden: true, type: :boolean, default: false, desc: "Automatically detect the repository branch from known CI environment variables or git CLI."
            method_option :tag, aliases: "-t", type: :array, banner: "TAG", desc: "Tag name for consumer version. Can be specified multiple times."
            method_option :tag_with_git_branch, aliases: "-g", type: :boolean, default: false, required: false, desc: "Tag consumer version with the name of the current git branch. Default: false"
            method_option :build_url, desc: "The build URL that created the pact"
            method_option :merge, type: :boolean, default: false, require: false, desc: "If a pact already exists for this consumer version and provider, merge the contents. Useful when running Pact tests concurrently on different build nodes."
            output_option_json_or_text
            shared_authentication_options

            def publish(*pact_files)
              require 'pact_broker/client/error'
              validate_credentials
              validate_pact_files(pact_files)
              result = publish_pacts(pact_files)
              $stdout.puts result.message
              exit(1) unless result.success
            rescue PactBroker::Client::Error => e
              raise PactPublicationError, "#{e.class} - #{e.message}"
            end

            desc 'list-latest-pact-versions', 'List the latest pact for each integration'
            shared_authentication_options
            output_option_json_or_table
            def list_latest_pact_versions(*required_but_ignored)
              require 'pact_broker/client/pacts/list_latest_versions'
              result = PactBroker::Client::Pacts::ListLatestVersions.call(options.broker_base_url, options.output, pact_broker_client_options)
              $stdout.puts result.message
              exit(1) unless result.success
            end

            no_commands do
              def validate_pact_files pact_files
                unless pact_files && pact_files.any?
                  raise ::Thor::RequiredArgumentMissingError, "No value provided for required pact_files"
                end
              end


              def publish_pacts pact_files
                require 'pact_broker/client/publish_pacts'

                write_options = options[:merge] ? { write: :merge } : {}
                consumer_version_params = {
                  number: options.consumer_app_version,
                  branch: branch,
                  tags: tags,
                  build_url: options.build_url,
                  version_required: (!!options.branch || !!options.build_url || explict_auto_detect_version_properties)
                }.compact

                PactBroker::Client::PublishPacts.call(
                  options.broker_base_url,
                  file_list(pact_files),
                  consumer_version_params,
                  { merge: options[:merge], output: options.output }.compact,
                  pact_broker_client_options.merge(write_options)
                )
              end

              def file_list pact_files
                require 'rake/file_list'

                correctly_separated_pact_files = pact_files.collect{ |path| path.gsub(/\\+/, '/') }
                paths = Rake::FileList[correctly_separated_pact_files].collect do | path |
                  if File.directory?(path)
                    Rake::FileList[File.join(path, "*.json")]
                  else
                    path
                  end
                end.flatten
                validate_pact_path_list(paths)
              end

              def validate_pact_path_list(paths)
                paths.collect do | path |
                  if File.exist?(path)
                    path
                  elsif path.start_with?("-")
                    raise Thor::Error.new("ERROR: pact-broker publish was called with invalid arguments #{[path]}")
                  else
                    raise Thor::Error.new("Specified pact file '#{path}' does not exist. This sometimes indicates one of the arguments has been specified with the wrong name and has been incorrectly identified as a file path.")
                  end
                end
              end

              def tags
                require 'pact_broker/client/git'

                t = [*options.tag]
                t << PactBroker::Client::Git.branch(raise_error: true) if options.tag_with_git_branch
                t.compact.uniq
              end

              def branch
                require 'pact_broker/client/git'

                if options.branch.nil? && options.auto_detect_version_properties
                  PactBroker::Client::Git.branch(raise_error: explict_auto_detect_version_properties)
                else
                  options.branch
                end
              end

              def explict_auto_detect_version_properties
                @explict_auto_detect_version_properties ||= ARGV.include?("--auto-detect-version-properties")
              end
            end
          end
        end
      end
    end
  end
end