package com.nike.wingtips.servlet.tag;

import com.nike.internal.util.StringUtils;
import com.nike.wingtips.servlet.HttpSpanFactory;
import com.nike.wingtips.tags.HttpTagAndSpanNamingAdapter;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Extension of {@link HttpTagAndSpanNamingAdapter} that knows how to handle Servlet {@link HttpServletRequest} and
 * {@link HttpServletResponse} objects.
 */
public class ServletRequestTagAdapter extends HttpTagAndSpanNamingAdapter<HttpServletRequest, HttpServletResponse> {

    @SuppressWarnings("WeakerAccess")
    protected static final ServletRequestTagAdapter DEFAULT_INSTANCE = new ServletRequestTagAdapter();

    /**
     * @return A reusable, thread-safe, singleton instance of this class that can be used by anybody who wants to use
     * this class and does not need any customization.
     */
    @SuppressWarnings("unchecked")
    public static ServletRequestTagAdapter getDefaultInstance() {
        return DEFAULT_INSTANCE;
    }

    /**
     * Since this class represents server requests/responses (not clients), we only want to consider HTTP status codes
     * greater than or equal to 500 to be an error. From a server's perspective, a 4xx response is the correct
     * response to a bad request, and should therefore not be considered an error (again, from the server's
     * perspective - the client may feel differently).
     *
     * @param response The response object.
     * @return The value of {@link #getResponseHttpStatus(HttpServletResponse)} if it is greater than or equal to 500,
     * or null otherwise.
     */
    @Override
    public @Nullable String getErrorResponseTagValue(@Nullable HttpServletResponse response) {
        Integer statusCode = getResponseHttpStatus(response);
        if (statusCode != null && statusCode >= 500) {
            return statusCode.toString();
        }

        // Status code does not indicate an error, so return null.
        return null;
    }

    @Override
    public @Nullable String getRequestUrl(@Nullable HttpServletRequest request) {
        if (request == null) {
            return null;
        }

        // request.getRequestURL() won't have a query string, so we need to separately look for it and add it
        //      if necessary.
        StringBuffer requestUrl = request.getRequestURL();
        String queryString = request.getQueryString();
        if (StringUtils.isNotBlank(queryString)) {
            requestUrl.append('?').append(queryString);
        }

        return requestUrl.toString();
    }

    @Override
    public @Nullable Integer getResponseHttpStatus(@Nullable HttpServletResponse response) {
        if (response == null) {
            return null;
        }

        return response.getStatus();
    }

    @Override
    public @Nullable String getRequestHttpMethod(@Nullable HttpServletRequest request) {
        if (request == null) {
            return null;
        }

        return request.getMethod();
    }


    @Override
    public @Nullable String getRequestPath(@Nullable HttpServletRequest request) {
        if (request == null) {
            return null;
        }

        return request.getRequestURI();
    }

    @Override
    public @Nullable String getRequestUriPathTemplate(
        @Nullable HttpServletRequest request,
        @Nullable HttpServletResponse response
    ) {
        return HttpSpanFactory.determineUriPathTemplate(request);
    }

    @Override
    public @Nullable String getHeaderSingleValue(@Nullable HttpServletRequest request, @NotNull String headerKey) {
        if (request == null) {
            return null;
        }

        return request.getHeader(headerKey);
    }

    @Override
    public @Nullable List<String> getHeaderMultipleValue(
        @Nullable HttpServletRequest request, @NotNull String headerKey
    ) {
        if (request == null) {
            return null;
        }

        Enumeration<String> matchingHeadersEnum = request.getHeaders(headerKey);

        if (matchingHeadersEnum == null) {
            return null;
        }

        return Collections.list(matchingHeadersEnum);
    }

    @Override
    public @Nullable String getSpanHandlerTagValue(
        @Nullable HttpServletRequest request, @Nullable HttpServletResponse response
    ) {
        return "servlet";
    }
}