/**
 *  Copyright 2005-2016 Red Hat, Inc.
 *
 *  Red Hat licenses this file to you under the Apache License, version
 *  2.0 (the "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *  implied.  See the License for the specific language governing
 *  permissions and limitations under the License.
 */
package io.fabric8.insight.log.storage;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import io.fabric8.insight.storage.StorageService;
import org.apache.felix.scr.annotations.*;
import org.ops4j.pax.logging.spi.PaxAppender;
import org.ops4j.pax.logging.spi.PaxLoggingEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.fabric8.insight.log.service.support.MavenCoordinates.addMavenCoord;
import static io.fabric8.insight.log.storage.InsightUtils.formatDate;
import static io.fabric8.insight.log.storage.InsightUtils.quote;

@Component(immediate = true, name = "io.fabric8.insight.log.storage.logs")
@Service(PaxAppender.class)
@Properties({
        @Property(name = "org.ops4j.pax.logging.appender.name", value = "InsightLogAppender")
})
public class InsightLogAppender implements PaxAppender {

    public static final String LOG_TYPE = "es.log.type";

    private static final Logger LOGGER = LoggerFactory.getLogger(InsightLogAppender.class);

    private String name;

    private String type = "log";

    @Reference
    private StorageService storageService;

    @Activate
    public void activate(Map<String, ?> configuration) {
        name = System.getProperty("runtime.id");
        if (configuration.containsKey(LOG_TYPE)) {
            type = (String) configuration.get(LOG_TYPE);
        }
    }

    @Modified
    public void modified(Map<String, ?> configuration) {
        if (configuration.containsKey(LOG_TYPE)) {
            type = (String) configuration.get(LOG_TYPE);
        } else {
            type = "log";
        }
    }

    public void doAppend(final PaxLoggingEvent paxLoggingEvent) {
        try {
            // Only store TRACE events which have a trace-id.
            if ("TRACE".equals(paxLoggingEvent.getLevel().toString().toUpperCase()) &&
                    !paxLoggingEvent.getProperties().containsKey("trace-id")) {
                return;
            }
            StringBuilder writer = new StringBuilder();
            writer.append("{ \"host\" : ");
            quote(name, writer);
            writer.append(",\n  \"@timestamp\" : ");
            quote(formatDate(paxLoggingEvent.getTimeStamp()), writer);
            writer.append(",\n  \"level\" : ");
            quote(paxLoggingEvent.getLevel().toString(), writer);
            writer.append(",\n  \"logger\" : ");
            quote(paxLoggingEvent.getLoggerName(), writer);
            writer.append(",\n  \"thread\" : ");
            quote(paxLoggingEvent.getThreadName(), writer);
            writer.append(",\n  \"message\" : ");
            quote(paxLoggingEvent.getMessage(), writer);

            String[] throwable = paxLoggingEvent.getThrowableStrRep();
            if (throwable != null) {
                throwable = addMavenCoord(throwable);
                writer.append(",\n  \"exception\" : [");
                for (int i = 0; i < throwable.length; i++) {
                    if (i != 0)
                        writer.append(", ");
                    quote(throwable[i], writer);
                }
                writer.append("]");
            }

            writer.append(",\n  \"properties\" : { ");
            boolean first = true;
            Map<String, String> properties = new HashMap<String, String>();
            Set<Map.Entry> set = paxLoggingEvent.getProperties().entrySet();
            for (Map.Entry entry : set) {
                Object key = entry.getKey();
                Object value = entry.getValue();
                if (key != null && value != null) {
                    properties.put(key.toString(), value.toString());
                }
            }
            addMavenCoord(properties);

            for (Object key : properties.keySet()) {
                if (first) {
                    first = false;
                } else {
                    writer.append(", ");
                }
                quote(key.toString(), writer);
                writer.append(": ");
                quote(properties.get(key).toString(), writer);
            }
            writer.append(" }");
            writer.append("\n}");

            if (type != null && storageService != null) {
                storageService.store(type, paxLoggingEvent.getTimeStamp(), writer.toString());
            }
        } catch (Exception e) {
            LOGGER.warn("Error appending log to storage", e);
        }
    }

}
