/**
 * Copyright (c) 2017 Bosch Software Innovations GmbH.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Bosch Software Innovations GmbH - initial creation
 */

package org.eclipse.hono.service.auth.delegating;

import java.util.Objects;

import org.eclipse.hono.auth.HonoUser;
import org.eclipse.hono.connection.ConnectionFactory;
import org.eclipse.hono.service.HealthCheckProvider;
import org.eclipse.hono.service.auth.AbstractHonoAuthenticationService;
import org.eclipse.hono.service.auth.AuthenticationConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.dns.DnsClient;
import io.vertx.core.impl.VertxInternal;
import io.vertx.ext.healthchecks.HealthCheckHandler;
import io.vertx.ext.healthchecks.Status;


/**
 * An authentication service that delegates authentication requests to a remote identity server.
 * <p>
 * This is the default authentication service for all Hono services.
 */
@Service
@Profile("!authentication-impl")
public class DelegatingAuthenticationService extends AbstractHonoAuthenticationService<AuthenticationServerClientConfigProperties> implements HealthCheckProvider {

    private AuthenticationServerClient client;
    private ConnectionFactory factory;
    private DnsClient dnsClient;

    @Autowired
    @Override
    public void setConfig(final AuthenticationServerClientConfigProperties configuration) {
        setSpecificConfig(configuration);
    }

    /**
     * Sets the DNS client to use for checking availability of an <em>Authentication</em> service.
     * <p>
     * If not set, the vert.x instance's address resolver is used instead.
     *
     * @param dnsClient The client.
     * @throws NullPointerException if client is {@code null}.
     */
    @Autowired(required = false)
    public void setDnsClient(final DnsClient dnsClient) {
        this.dnsClient = Objects.requireNonNull(dnsClient);
    }

    /**
     * Sets the factory to use for connecting to the authentication server.
     * 
     * @param connectionFactory The factory.
     * @throws NullPointerException if the factory is {@code null}.
     */
    @Autowired
    public void setConnectionFactory(@Qualifier(AuthenticationConstants.QUALIFIER_AUTHENTICATION) final ConnectionFactory connectionFactory) {
        this.factory = Objects.requireNonNull(connectionFactory);
    }

    /**
     * This method does not register any specific liveness checks.
     */
    @Override
    public void registerLivenessChecks(final HealthCheckHandler livenessHandler) {
        // do not register any specific checks
    }

    /**
     * Registers a check which succeeds if a connection with the configured <em>Authentication</em> service can be established.
     *
     * @param readinessHandler The health check handler to register the checks with.
     */
    @Override
    public void registerReadinessChecks(final HealthCheckHandler readinessHandler) {

        if (dnsClient != null) {
            log.info("registering readiness check using DNS Client");
            readinessHandler.register("authentication-service-availability", status -> {
                log.trace("checking availability of Authentication service");
                dnsClient.lookup(getConfig().getHost(), lookupAttempt -> {
                    if (lookupAttempt.succeeded()) {
                        status.tryComplete(Status.OK());
                    } else {
                        log.debug("readiness check failed to resolve Authentication service address [{}]: ",
                                getConfig().getHost(), lookupAttempt.cause().getMessage());
                        status.tryComplete(Status.KO());
                    }
                });
            });
        } else if (VertxInternal.class.isInstance(vertx)) {
            log.info("registering readiness check using vert.x Address Resolver");
            readinessHandler.register("authentication-service-availability", status -> {
                log.trace("checking availability of Authentication service");
                ((VertxInternal) vertx).resolveAddress(getConfig().getHost(), lookupAttempt -> {
                    if (lookupAttempt.succeeded()) {
                        status.tryComplete(Status.OK());
                    } else {
                        log.debug("readiness check failed to resolve Authentication service address [{}]: ",
                                getConfig().getHost(), lookupAttempt.cause().getMessage());
                        status.tryComplete(Status.KO());
                    };
                });
            });
        } else {
            log.warn("cannot register readiness check, no DNS resolver available");
        }
    }

    @Override
    protected void doStart(final Future<Void> startFuture) {
        if (factory == null) {
            startFuture.fail("no connection factory for authentication server set");
        } else {
            client = new AuthenticationServerClient(vertx, factory);
            startFuture.complete();
        }
    }

    @Override
    public void verifyExternal(final String authzid, final String subjectDn, final Handler<AsyncResult<HonoUser>> authenticationResultHandler) {
        client.verifyExternal(authzid, subjectDn, authenticationResultHandler);
    }

    @Override
    public void verifyPlain(final String authzid, final String authcid, final String password,
            final Handler<AsyncResult<HonoUser>> authenticationResultHandler) {

        client.verifyPlain(authzid, authcid, password, authenticationResultHandler);
    }

    @Override
    public String toString() {
        return new StringBuilder(DelegatingAuthenticationService.class.getSimpleName())
                .append("[Authentication service: ").append(getConfig().getHost()).append(":").append(getConfig().getPort()).append("]")
                .toString();
    }
}

@@ -5336,17 +5336,17 @@
 ory for 
-a
+A
 uthentic
@@ -5355,18 +5355,19 @@
 ion serv
+ic
 e
-r
  set%22);%0A

Improve logging.