package io.opentracing.contrib.web.servlet.filter.decorator;

import io.opentracing.Span;
import io.opentracing.contrib.web.servlet.filter.ServletFilterSpanDecorator;
import io.opentracing.tag.StringTag;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * ServletFilterHeaderSpanDecorator will decorate the span based on incoming HTTP headers.
 * Incoming are compared to the list of {@link #allowedHeaders}, if the header is part of the provided list,
 * they will be added as {@link StringTag}.
 * The tag format will be a concatenation of {@link #prefix} and {@link HeaderEntry#tag}
 */
public class ServletFilterHeaderSpanDecorator implements ServletFilterSpanDecorator {

    private final String prefix;
    private final List<HeaderEntry> allowedHeaders;

    /**
     * Constructor of ServletFilterHeaderSpanDecorator with a default prefix of "http.header."
     * @param allowedHeaders list of {@link HeaderEntry} to extract from the incoming request
     */
    public ServletFilterHeaderSpanDecorator(List<HeaderEntry> allowedHeaders) {
        this(allowedHeaders, "http.header.");
    }

    /**
     * Constructor of ServletFilterHeaderSpanDecorator
     * @param allowedHeaders list of {@link HeaderEntry} to extract from the incoming request
     * @param prefix the prefix to prepend on each @{@link StringTag}. Can be null is not prefix is desired
     */
    public ServletFilterHeaderSpanDecorator(List<HeaderEntry> allowedHeaders, String prefix) {
        this.allowedHeaders = new ArrayList<>(allowedHeaders);
        this.prefix = (prefix != null && !prefix.isEmpty()) ? prefix : null;
    }

    @Override
    public void onRequest(HttpServletRequest httpServletRequest, Span span) {
        for (HeaderEntry headerEntry : allowedHeaders) {
            String headerValue = httpServletRequest.getHeader(headerEntry.getHeader());
            if (headerValue != null && !headerValue.isEmpty()) {
                buildTag(headerEntry.getTag()).set(span, headerValue);
            }
        }
    }

    @Override
    public void onResponse(HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse, Span span) {
    }

    @Override
    public void onError(HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse, Throwable exception, Span span) {
    }

    @Override
    public void onTimeout(HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse, long timeout, Span span) {
    }

    private StringTag buildTag(String tag) {
        if (prefix == null) {
            return new StringTag(tag);
        }
        return new StringTag(prefix + tag);
    }

    public String getPrefix() {
        return this.prefix;
    }

    public List<HeaderEntry> getAllowedHeaders() {
        return this.allowedHeaders;
    }

    /**
     * HeaderEntry is used to configure {@link ServletFilterHeaderSpanDecorator}
     * {@link #header} is used to check if the header exists using {@link HttpServletRequest#getHeader(String)}
     * {@link #tag} will be used as a {@link StringTag} if {@link #header} is found on the incoming request
     */
    public static class HeaderEntry {
        private final String header;
        private final String tag;

        /**
         * @param header Header on the {@link HttpServletRequest}
         * @param tag Tag to be used if {@link #header} is found
         */
        public HeaderEntry(String header, String tag) {
            this.header = header;
            this.tag = tag;
        }
        public String getHeader() {
            return this.header;
        }

        public String getTag() {
            return this.tag;
        }

    }

}
