// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.service.slobrok;

import com.google.inject.Inject;
import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.jrt.slobrok.api.Mirror;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.applicationmodel.ClusterId;
import com.yahoo.vespa.applicationmodel.ConfigId;
import com.yahoo.vespa.applicationmodel.ServiceStatus;
import com.yahoo.vespa.applicationmodel.ServiceStatusInfo;
import com.yahoo.vespa.applicationmodel.ServiceType;
import com.yahoo.vespa.service.duper.DuperModelManager;
import com.yahoo.vespa.service.manager.MonitorManager;
import com.yahoo.vespa.service.monitor.SlobrokApi;

import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.logging.Logger;

public class SlobrokMonitorManagerImpl implements SlobrokApi, MonitorManager {
    private static final Logger logger =
            Logger.getLogger(SlobrokMonitorManagerImpl.class.getName());

    private final Supplier<SlobrokMonitor> slobrokMonitorFactory;

    private final Object monitor = new Object();
    private final HashMap<ApplicationId, SlobrokMonitor> slobrokMonitors = new HashMap<>();
    private final DuperModelManager duperModel;

    @Inject
    public SlobrokMonitorManagerImpl(DuperModelManager duperModel) {
        this(SlobrokMonitor::new, duperModel);
    }

    SlobrokMonitorManagerImpl(Supplier<SlobrokMonitor> slobrokMonitorFactory, DuperModelManager duperModel) {
        this.slobrokMonitorFactory = slobrokMonitorFactory;
        this.duperModel = duperModel;
    }

    @Override
    public void applicationActivated(ApplicationInfo application) {
        if (!wouldMonitor(application.getApplicationId())) {
            return;
        }

        synchronized (monitor) {
            SlobrokMonitor slobrokMonitor = slobrokMonitors.computeIfAbsent(
                    application.getApplicationId(),
                    id -> slobrokMonitorFactory.get());
            slobrokMonitor.updateSlobrokList(application);
        }
    }

    @Override
    public void applicationRemoved(ApplicationId id) {
        if (!wouldMonitor(id)) {
            return;
        }

        synchronized (monitor) {
            SlobrokMonitor slobrokMonitor = slobrokMonitors.remove(id);
            if (slobrokMonitor == null) {
                logger.log(LogLevel.WARNING, "Removed application " + id +
                        ", but it was never registered");
            } else {
                slobrokMonitor.close();
            }
        }
    }

    @Override
    public List<Mirror.Entry> lookup(ApplicationId id, String pattern) {
        synchronized (monitor) {
            SlobrokMonitor slobrokMonitor = slobrokMonitors.get(id);
            if (slobrokMonitor == null) {
                throw new IllegalArgumentException("Slobrok manager has no knowledge of application " + id);
            } else {
                return slobrokMonitor.lookup(pattern);
            }
        }
    }

    @Override
    public ServiceStatusInfo getStatus(ApplicationId applicationId,
                                       ClusterId clusterId,
                                       ServiceType serviceType,
                                       ConfigId configId) {
        if (!wouldMonitor(applicationId)) {
            return new ServiceStatusInfo(ServiceStatus.NOT_CHECKED);
        }

        Optional<String> slobrokServiceName = findSlobrokServiceName(serviceType, configId);
        if (slobrokServiceName.isPresent()) {
            synchronized (monitor) {
                SlobrokMonitor slobrokMonitor = slobrokMonitors.get(applicationId);
                if (slobrokMonitor != null && slobrokMonitor.registeredInSlobrok(slobrokServiceName.get())) {
                    return new ServiceStatusInfo(ServiceStatus.UP);
                } else {
                    return new ServiceStatusInfo(ServiceStatus.DOWN);
                }
            }
        } else {
            return new ServiceStatusInfo(ServiceStatus.NOT_CHECKED);
        }
    }

    private boolean wouldMonitor(ApplicationId applicationId) {
        if (duperModel.isSupportedInfraApplication(applicationId)) {
            return false;
        }

        return true;
    }

    /**
     * Get the Slobrok service name of the service, or empty if the service
     * is not registered with Slobrok.
     */
    Optional<String> findSlobrokServiceName(ServiceType serviceType, ConfigId configId) {
        switch (serviceType.s()) {
            case "adminserver":
            case "config-sentinel":
            case "configproxy":
            case "configserver":
            case "logd":
            case "logserver":
            case "metricsproxy":
            case "slobrok":
            case "transactionlogserver":
                return Optional.empty();

            case "topleveldispatch":
                return Optional.of(configId.s());

            case "qrserver":
            case "container":
            case "container-clustercontroller":
            case "metricsproxy-container":
                return Optional.of("vespa/service/" + configId.s());

            case "searchnode": //TODO: handle only as storagenode instead of both as searchnode/storagenode
                return Optional.of(configId.s() + "/realtimecontroller");
            case "distributor":
            case "storagenode":
                return Optional.of("storage/cluster." + configId.s());
            default:
                logger.log(LogLevel.DEBUG, "Unknown service type " + serviceType.s() +
                        " with config id " + configId.s());
                return Optional.empty();
        }
    }
}

@@ -1823,38 +1823,40 @@
 ) %7B%0A        if (
-!
 would
+Not
 Monitor(applicat
@@ -2280,38 +2280,40 @@
 ) %7B%0A        if (
-!
 would
+Not
 Monitor(id)) %7B%0A 
@@ -3443,14 +3443,16 @@
 if (
-!
 would
+Not
 Moni
@@ -4246,16 +4246,19 @@
 an would
+Not
 Monitor(
@@ -4296,20 +4296,23 @@
         
-if (
+return 
 duperMod
@@ -4360,68 +4360,8 @@
 nId)
-) %7B%0A            return false;%0A        %7D%0A%0A        return true
 ;%0A  
@@ -5092,32 +5092,32 @@
 se %22container%22:%0A
-
             case
@@ -5144,24 +5144,64 @@
 ontroller%22:%0A
+            case %22logserver-container%22:%0A
             

Add logserver container to slobrok monitor manager