require 'uri'
require 'pact_broker/client/hal'
require 'ostruct'
require 'json'
require 'pact_broker/client/command_result'

module PactBroker
  module Client
    module Webhooks
      class Create
        WEBHOOKS_WITH_OPTIONAL_PACTICICPANTS_NOT_SUPPORTED = "This version of the Pact Broker requires that both consumer and provider are specified for a webhook. Please upgrade your broker to >= 2.22.0 to create a webhook with optional consumer and provider."
        CREATING_WEBHOOK_WITH_UUID_NOT_SUPPORTED = "This version of the Pact Broker does not support creating webhooks with a specified UUID. Please upgrade your broker to >= 2.49.0 or use the create-webhook command."

        attr_reader  :params, :pact_broker_base_url, :basic_auth_options, :verbose

        def self.call(params, pact_broker_base_url, pact_broker_client_options)
          new(params, pact_broker_base_url, pact_broker_client_options).call
        end

        def initialize(params, pact_broker_base_url, pact_broker_client_options)
          @params = OpenStruct.new(params)
          @pact_broker_base_url = pact_broker_base_url
          @http_client = PactBroker::Client::Hal::HttpClient.new(pact_broker_client_options.merge(pact_broker_client_options[:basic_auth] || {}))
        end

        def call
          if params.consumer && params.provider && !params.uuid
            create_webhook_with_consumer_and_provider
          else
            create_webhook_with_optional_consumer_and_provider
          end
        end

        private

        attr_reader :http_client

        def create_webhook_with_consumer_and_provider
          webhook_entity = webhook_link.expand(consumer: params.consumer, provider: params.provider).post(request_body)
          handle_response(webhook_entity)
        end

        def create_webhook_with_optional_consumer_and_provider
          index_entity = index_link.get!
          if params.uuid
            if index_entity.can?("pb:webhook")
              webhook_entity = index_entity._link("pb:webhook").expand(uuid: params.uuid).put(request_body_with_optional_consumer_and_provider)
            else
              return error_result(CREATING_WEBHOOK_WITH_UUID_NOT_SUPPORTED)
            end
          else
            webhook_entity = index_entity._link("pb:webhooks").post(request_body_with_optional_consumer_and_provider)
          end

          if webhook_entity.response.status == 405
            return error_result(WEBHOOKS_WITH_OPTIONAL_PACTICICPANTS_NOT_SUPPORTED)
          end

          handle_response(webhook_entity)
        end

        def request_body
          webhook_request_body = JSON.parse(params.body) rescue params.body
          {
            events: params.events.collect{ | event | { "name" => event }},
            request: {
              url: params.url,
              method: params.http_method,
              headers: params.headers,
              body: webhook_request_body,
              username: params.username,
              password: params.password
            }
          }.tap { |req| req[:description] = params.description if params.description }
        end

        def request_body_with_optional_consumer_and_provider
          body = request_body

          if params.consumer
            body[:consumer] = { name: params.consumer }
          end

          if params.provider
            body[:provider] = { name: params.provider }
          end

          body
        end

        def webhook_for_consumer_and_provider_url
          "#{pact_broker_base_url.chomp("/")}/webhooks/provider/{provider}/consumer/{consumer}"
        end

        def handle_response(webhook_entity)
          if webhook_entity.success?
            success_result(webhook_entity)
          else
            http_error_result(webhook_entity)
          end
        end

        def success_result(webhook_entity)
          CommandResult.new(true, "Webhook #{webhook_entity._link('self').title_or_name.inspect} created")
        end

        def error_result(message)
          CommandResult.new(false, message)
        end

        def http_error_result(webhook_entity)
          CommandResult.new(false, "Error creating webhook. response status=#{webhook_entity.response.status} body=#{webhook_entity.response.raw_body}")
        end

        def index_link
          PactBroker::Client::Hal::EntryPoint.new(pact_broker_base_url, http_client)
        end

        def webhook_link
          PactBroker::Client::Hal::EntryPoint.new(webhook_for_consumer_and_provider_url, http_client)
        end
      end
    end
  end
end