package com.netflix.suro.sink;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Inject;
import com.netflix.config.DynamicStringProperty;
import com.netflix.governator.annotations.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.util.Map;

/**
 * {@link com.netflix.config.DynamicProperty} driven sink configuration.  Whenever a change is made to the
 * dynamic property, this module will parse the JSON and set a new configuration on the
 * main SinkManager.
 *
 * @author elandau
 */
public class DynamicPropertySinkConfigurator {
    private static final Logger log = LoggerFactory.getLogger(DynamicPropertySinkConfigurator.class);

    public static final String SINK_PROPERTY = "SuroServer.sinkConfig";

    private final SinkManager     sinkManager;
    private final ObjectMapper    jsonMapper;

    @Configuration(SINK_PROPERTY)
    private String initialSink;

    @Inject
    public DynamicPropertySinkConfigurator(
        SinkManager sinkManager,
        ObjectMapper jsonMapper) {
        this.sinkManager = sinkManager;
        this.jsonMapper  = jsonMapper;
    }

    @PostConstruct
    public void init() {
        DynamicStringProperty sinkDescription = new DynamicStringProperty(SINK_PROPERTY, initialSink) {
            @Override
            protected void propertyChanged() {
                buildSink(get(), false);
            }
        };

        buildSink(sinkDescription.get(), true);
    }

    private void buildSink(String sink, boolean initStart) {
        try {
            Map<String, Sink> newSinkMap = jsonMapper.readValue(sink, new TypeReference<Map<String, Sink>>(){});
            if ( !newSinkMap.containsKey("default") ) {
                throw new IllegalStateException("default sink should be defined");
            }

            if (initStart) {
                sinkManager.initialSet(newSinkMap);
            } else {
                sinkManager.set(newSinkMap);
            }
        } catch (Exception e) {
            log.error("Exception on building SinkManager: " + e.getMessage(), e);
        }
    }
}