package com.eactive.testmaster.api.dto;

import io.netty.util.CharsetUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;

public class MockRequest {

    private static final Charset DEFAULT_CHARSET = determineDefaultCharset();

    private static Charset determineDefaultCharset() {
        try {
            String systemEncoding = System.getProperty("file.encoding");
            if (StringUtils.isNotBlank(systemEncoding)) {
                return Charset.forName(systemEncoding);
            }
        } catch (IllegalArgumentException | SecurityException e) {
            // Log warning about fallback to UTF-8
        }
        return CharsetUtil.UTF_8;
    }

    public static Map<String, String> parseQueryString(HttpServletRequest request) {
        Map<String, String> queryParams = new HashMap<>();
        String queryString = request.getQueryString() != null ? URLDecoder.decode(request.getQueryString()) : null;
        if (queryString != null) {
            String[] queryParamsArr = queryString.split("&");
            for (String queryParam : queryParamsArr) {
                String[] pair = queryParam.split("=");
                String key = pair[0];
                String value = "";
                if (pair.length > 1) {
                    value = pair[1];
                }
                queryParams.put(key, value);
            }
        }
        return queryParams;
    }

    public static final Pattern CHARSET_PATTERN = Pattern.compile("charset=([^;]+)");

    private Charset extractCharset(Map<String, String> headers) {
        String contentType = headers.get("content-type");

        if (contentType != null) {
            Matcher matcher = CHARSET_PATTERN.matcher(contentType.toLowerCase());
            if (matcher.find()) {
                try {
                    return Charset.forName(matcher.group(1).trim());
                } catch (IllegalArgumentException e) {
                    // Log warning about unsupported charset
                    return DEFAULT_CHARSET;
                }
            }
        }

        return DEFAULT_CHARSET;
    }

    public MockRequest(HttpServletRequest request) throws IOException {
        String contextPath = request.getContextPath();
        this.method = request.getMethod();
        this.uri = request.getRequestURI().replace(contextPath, "");
        this.headers = Collections.list(request.getHeaderNames()).stream()
            .collect(Collectors.toMap(name -> name, request::getHeader));

        this.params = parseQueryString(request);

        String charset = extractCharset(headers).toString();

        try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(request.getInputStream(), charset))) {
            String rawBody = reader.lines()
                .collect(Collectors.joining(System.lineSeparator()));

            // Only URL decode if the content type indicates form submission
            String contentType = request.getContentType();
            if (contentType != null) {
                this.body = URLDecoder.decode(rawBody, charset);
            } else {
                this.body = rawBody;
            }
        }
    }

    private String uri;
    private String method;
    private String body;

    private Map<String, String> headers;

    private Map<String, String> params;

    public String getUri() {
        return uri;
    }

    public void setUri(String uri) {
        this.uri = uri;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public String getHeader(String key) {
        return this.headers.get(key);
    }

    public String getParam(String key) {
        return this.params.get(key);
    }

    public Map<String, String> getParams() {
        return params;
    }

    public void setParams(Map<String, String> params) {
        this.params = params;
    }
}
