package org.jboss.resteasy.plugins.server.servlet;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;

import org.jboss.resteasy.core.Headers;
import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
import org.jboss.resteasy.specimpl.ResteasyUriInfo;
import org.jboss.resteasy.util.HttpHeaderNames;
import org.jboss.resteasy.util.MediaTypeHelper;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class ServletUtil {
    public static ResteasyUriInfo extractUriInfo(HttpServletRequest request, String servletPrefix) {
        String contextPath = request.getContextPath();
        if (servletPrefix != null && servletPrefix.length() > 0 && !servletPrefix.equals("/")) {
            if (!contextPath.endsWith("/") && !servletPrefix.startsWith("/"))
                contextPath += "/";
            contextPath += servletPrefix;
        }
        String queryString = request.getQueryString();
        String absolute;
        if (queryString != null && queryString.length() > 0) {
            absolute = request.getRequestURL().append('?').append(queryString).toString();
        } else {
            absolute = request.getRequestURL().toString();
        }
        if (!absolute.contains(contextPath)) {
            String encodedContextPath = Arrays.stream(contextPath.substring(1).split("/"))
                    .map(s -> {
                        try {
                            return URLEncoder.encode(s, "UTF-8");
                        } catch (UnsupportedEncodingException ex) {
                            return s;
                        }
                    })
                    .collect(Collectors.joining("/", "/", ""));
            if (absolute.contains(encodedContextPath)) {
                absolute = absolute.replace(encodedContextPath, contextPath);
            }
        }
        return new ResteasyUriInfo(absolute, contextPath);
    }

    public static ResteasyHttpHeaders extractHttpHeaders(HttpServletRequest request) {

        MultivaluedMap<String, String> requestHeaders = extractRequestHeaders(request);
        ResteasyHttpHeaders headers = new ResteasyHttpHeaders(requestHeaders);

        String contentType = request.getContentType();
        if (contentType != null)
            headers.getMutableHeaders().putSingle(HttpHeaders.CONTENT_TYPE, contentType);

        Map<String, Cookie> cookies = extractCookies(request);
        headers.setCookies(cookies);

        // test parsing should throw an exception on error
        headers.testParsing();

        return headers;

    }

    static Map<String, Cookie> extractCookies(HttpServletRequest request) {
        Map<String, Cookie> cookies = new HashMap<String, Cookie>();
        if (request.getCookies() != null) {
            for (jakarta.servlet.http.Cookie cookie : request.getCookies()) {
                Cookie ck1 = new Cookie.Builder(cookie.getName())
                        .value(cookie.getValue())
                        .path(cookie.getPath())
                        .domain(cookie.getDomain())
                        .version(cookie.getVersion())
                        .build();
                cookies.put(cookie.getName(), ck1);

            }
        }
        return cookies;
    }

    public static List<MediaType> extractAccepts(MultivaluedMap<String, String> requestHeaders) {
        List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
        List<String> accepts = requestHeaders.get(HttpHeaderNames.ACCEPT);
        if (accepts == null)
            return acceptableMediaTypes;

        for (String accept : accepts) {
            acceptableMediaTypes.addAll(MediaTypeHelper.parseHeader(accept));
        }
        return acceptableMediaTypes;
    }

    public static List<String> extractLanguages(MultivaluedMap<String, String> requestHeaders) {
        List<String> acceptable = new ArrayList<String>();
        List<String> accepts = requestHeaders.get(HttpHeaderNames.ACCEPT_LANGUAGE);
        if (accepts == null)
            return acceptable;

        for (String accept : accepts) {
            String[] splits = accept.split(",");
            for (String split : splits)
                acceptable.add(split.trim());
        }
        return acceptable;
    }

    public static MultivaluedMap<String, String> extractRequestHeaders(HttpServletRequest request) {
        Headers<String> requestHeaders = new Headers<String>();

        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            Enumeration<String> headerValues = request.getHeaders(headerName);
            while (headerValues.hasMoreElements()) {
                String headerValue = headerValues.nextElement();
                requestHeaders.add(headerName, headerValue);
            }
        }
        return requestHeaders;
    }
}
