package org.mockserver.dashboard;

import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.lang3.StringUtils.substringAfter;
import static org.apache.commons.lang3.StringUtils.substringAfterLast;
import static org.mockserver.mock.HttpState.PATH_PREFIX;
import static org.mockserver.model.HttpResponse.notFoundResponse;
import static org.mockserver.model.HttpResponse.response;

/**
 * @author jamesdbloom
 */
public class DashboardHandler {

    private static final Map<String, String> MIME_MAP = new HashMap<>();
    private static final List<String> IS_STRING_CONTENT = ImmutableList.of(
        "css",
        "js",
        "map",
        "json",
        "html"
    );

    public DashboardHandler() {
        MIME_MAP.put("css", "text/css; charset=utf-8");
        MIME_MAP.put("js", "application/javascript; charset=UTF-8");
        MIME_MAP.put("map", "application/json; charset=UTF-8");
        MIME_MAP.put("json", "application/json; charset=UTF-8");
        MIME_MAP.put("html", "text/html; charset=utf-8");
        MIME_MAP.put("ico", "image/x-icon");
        MIME_MAP.put("woff2", "application/font-woff2");
        MIME_MAP.put("ttf", "application/octet-stream");
        MIME_MAP.put("png", "image/png");
    }

    public void renderDashboard(final ChannelHandlerContext ctx, final HttpRequest request) throws Exception {
        HttpResponse response = notFoundResponse();
        if (request.getMethod().getValue().equals("GET")) {
            String path = substringAfter(request.getPath().getValue(), PATH_PREFIX + "/dashboard");
            if (path.isEmpty() || path.equals("/")) {
                path = "/index.html";
            }
            if (path.contains("..")) {
                ctx.writeAndFlush(notFoundResponse()).addListener(ChannelFutureListener.CLOSE);
                return;
            }
            String resourcePath = "/org/mockserver/dashboard" + path;
            String normalizedPath = java.net.URI.create(resourcePath).normalize().getPath();
            if (!normalizedPath.startsWith("/org/mockserver/dashboard/") && !normalizedPath.equals("/org/mockserver/dashboard")) {
                ctx.writeAndFlush(notFoundResponse()).addListener(ChannelFutureListener.CLOSE);
                return;
            }
            try (InputStream contentStream = DashboardHandler.class.getResourceAsStream(normalizedPath)) {
                if (contentStream != null) {
                    final String extension = substringAfterLast(path, ".");
                    if (IS_STRING_CONTENT.contains(extension)) {
                        final String content = new String(ByteStreams.toByteArray(contentStream), UTF_8.name());
                        response =
                            response()
                                .withHeader(HttpHeaderNames.CONTENT_TYPE.toString(), MIME_MAP.get(extension))
                                .withHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), String.valueOf(content.getBytes().length))
                                .withBody(content);
                    } else {
                        final byte[] bytes = ByteStreams.toByteArray(contentStream);
                        response =
                            response()
                                .withHeader(HttpHeaderNames.CONTENT_TYPE.toString(), MIME_MAP.get(extension))
                                .withHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), String.valueOf(bytes.length))
                                .withBody(bytes);
                    }
                    if (request.isKeepAlive()) {
                        response.withHeader(HttpHeaderNames.CONNECTION.toString(), HttpHeaderValues.KEEP_ALIVE.toString());
                    }
                }
            }
        }
        if (!request.isKeepAlive()) {
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            ctx.writeAndFlush(response);
        }
    }
}
