module ThreeScale
  module OAuth2
    module Client
      module_function

      Config = Struct.new(:identifier_key, :user_info_url)
      Options = Struct.new(:site, :token_url, :authorize_url) do
        def to_hash
          { site: site, token_url: token_url, authorize_url: authorize_url }.reject { |_, v| !v }
        end
      end

      Authentication = Struct.new(:system_name, :credentials, :options, :config) do
        delegate :client_id, :client_secret, to: :credentials
        delegate :identifier_key, :user_info_url, to: :config
      end

      # @param [::AuthenticationProvider] authentication_provider
      # @return [::ThreeScale::OAuth::ClientBase]
      # @return [::ThreeScale::OAuth::GithubClient]
      # @return [::ThreeScale::OAuth::Auth0Client]
      def build(authentication_provider)
        authentication = build_authentication(authentication_provider)

        case authentication_provider.read_attribute(:kind)
        when 'github'.freeze
            ThreeScale::OAuth2::GithubClient.new(authentication)
        when 'auth0'.freeze
            ThreeScale::OAuth2::Auth0Client.new(authentication)
        when 'keycloak'.freeze
            ThreeScale::OAuth2::KeycloakClient.new(authentication)
        else
            ThreeScale::OAuth2::ClientBase.new(authentication)
        end
      end

      # @param [::AuthenticationProvider] authentication_provider
      def build_authentication(authentication_provider)
        credentials = authentication_provider.credentials
        system_name = authentication_provider.system_name

        options = Options.new(authentication_provider.site.presence,
                              authentication_provider.token_url.presence,
                              authentication_provider.authorize_url.presence)

        config = Config.new(authentication_provider.identifier_key,
                                   authentication_provider.user_info_url)

        Authentication.new(system_name, credentials, options, config)
      end
    end
  end
end
