require 'json'
require 'pact/errors'

module Pact
  module Provider
    module VerificationResults

      class PublicationError < Pact::Error; end

      class Publish

        def self.call pact_source, verification_result
          new(pact_source, verification_result).call
        end

        def initialize pact_source, verification_result
          @pact_source = pact_source
          @verification_result = verification_result
        end

        def call
          if Pact.configuration.provider.publish_verification_results?
            if publication_url
              publish
            else
              Pact.configuration.error_stream.puts "WARN: Cannot publish verification for #{consumer_name} as there is no link named pb:publish-verification-results in the pact JSON. If you are using a pact broker, please upgrade to version 2.0.0 or later."
            end
          end
        end

        private

        def publication_url
          @publication_url ||= pact_source.pact_hash.fetch('_links', {}).fetch('pb:publish-verification-results', {})['href']
        end

        def publish
          #TODO https
          #TODO username/password
          uri = URI(publication_url)
          request = build_request(uri)
          response = nil
          begin
            options = {:use_ssl => uri.scheme == 'https'}
            response = Net::HTTP.start(uri.host, uri.port, options) do |http|
              http.request request
            end
          rescue StandardError => e
            error_message = "Failed to publish verification result due to: #{e.class} #{e.message}"
            raise PublicationError.new(error_message)
          end

          unless response.code.start_with?("2")
            raise PublicationError.new("Error returned from verification result publication #{response.code} #{response.body}")
          end
        end

        def build_request uri
          request = Net::HTTP::Post.new(uri.path)
          request['Content-Type'] = "application/json"
          request.body = verification_result.to_json
          debug_uri = uri
          if pact_source.uri.basic_auth?
            request.basic_auth pact_source.uri.username, pact_source.uri.password
            debug_uri = URI(uri.to_s).tap { |x| x.userinfo="#{pact_source.uri.username}:*****"}
          end
          Pact.configuration.output_stream.puts "INFO: Publishing verification result #{verification_result.to_json} to #{debug_uri}"
          request
        end

        def consumer_name
          pact_source.pact_hash['consumer']['name']
        end

        attr_reader :pact_source, :verification_result
      end
    end
  end
end