package org.wildfly.swarm.fluentd.runtime;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.ErrorManager;
import java.util.logging.Logger;

import org.fluentd.logger.sender.RawSocketSender;
import org.jboss.logmanager.ExtHandler;
import org.jboss.logmanager.ExtLogRecord;

/**
 * @author Heiko Braun
 * @since 14/11/2016
 */
public class FluentdHandler extends ExtHandler {

    public enum Key {
        EXCEPTION("exception"),
        LEVEL("level"),
        LOGGER_CLASS_NAME("loggerClassName"),
        LOGGER_NAME("loggerName"),
        MDC("mdc"),
        MESSAGE("message"),
        NDC("ndc"),
        RECORD("record"),
        SEQUENCE("sequence"),
        THREAD_ID("threadId"),
        THREAD_NAME("threadName"),
        TIMESTAMP("timestamp");

        private final String key;

        Key(final String key) {
            this.key = key;
        }

        /**
         * Returns the name of the key for the structure.
         *
         * @return the name of they key
         */
        public String getKey() {
            return key;
        }
    }

    public FluentdHandler() {
        setAutoFlush(false);
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    @Override
    protected void doPublish(ExtLogRecord record) {

        synchronized (this) {
            if (!initialized) {
                try {
                    initialize();
                } catch (Exception e) {
                    reportError("Error creating fluentd connection", e, ErrorManager.OPEN_FAILURE);
                    setEnabled(false);
                }
            }
        }

        if (initialized) {
            Map<String, Object> entries = new HashMap<>();

            entries.put(Key.SEQUENCE.getKey(), record.getSequenceNumber());
            entries.put(Key.LEVEL.getKey(), record.getLevel().getName());
            entries.put(Key.THREAD_NAME.getKey(), record.getThreadName());
            entries.put(Key.MESSAGE.getKey(), record.getFormattedMessage());
            entries.put(Key.THREAD_ID.getKey(), record.getThreadID());
            entries.put(Key.MDC.getKey(), record.getMdcCopy());
            entries.put(Key.NDC.getKey(), record.getNdc());

            this.sender.emit(this.tag, record.getMillis(), entries);
        }

    }

    private void initialize() {
        try {
            this.sender = new RawSocketSender(hostname, port);
            this.initialized = true;
            log.info("Connected to fluentd daemon");
        } catch (Throwable t) {
            throw new RuntimeException("Failed to initialise fluentd connection", t);
        }
    }

    @Override
    public void flush() {
        // should not happen
    }

    @Override
    public void close() {
        super.close();
        log.info("Disconnect from fluentd daemon ...");
        synchronized (this) {
            safeClose(this.sender);
            this.sender = null;
            this.initialized = false;
        }
    }

    private void safeClose(RawSocketSender c) {
        try {
            if (c != null) {
                c.close();
            }
        } catch (Exception e) {
            reportError("Error closing resource", e, ErrorManager.CLOSE_FAILURE);
        } catch (Throwable ignored) {
        }
    }

    private static final java.util.logging.Logger log = Logger.getLogger("org.wildfly.swarm.fluentd");

    private String hostname;

    private int port;

    private boolean initialized;

    private RawSocketSender sender;

    private String tag;

}
