package com.pandoli365.bibimbap.controller.api;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.util.UriUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.UUID;

@Controller
public class GameAssetController {

    @Value("${app.upload.game-storage-path:src/main/resources/static}")
    private String uploadStoragePath;

    @Value("${app.webgl.frame-ancestors:self}")
    private String webglFrameAncestors;

    @GetMapping("/game/{gameUuid}/**")
    public void gameAsset(
            @PathVariable("gameUuid") String gameUuid,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws IOException {
        String normalizedGameUuid = normalizeGameUuid(gameUuid);
        if (normalizedGameUuid == null) {
            response.sendError(HttpStatus.NOT_FOUND.value());
            return;
        }

        Path gameDir = gameRoot().resolve(normalizedGameUuid).normalize();
        Path assetFile = resolveAssetFile(gameDir, normalizedGameUuid, request);
        if (assetFile == null || !Files.isRegularFile(assetFile)) {
            response.sendError(HttpStatus.NOT_FOUND.value());
            return;
        }

        String contentEncoding = contentEncoding(assetFile);
        String contentType = contentType(assetFile);
        response.setStatus(HttpStatus.OK.value());
        if (contentEncoding != null) {
            response.setHeader(HttpHeaders.CONTENT_ENCODING, contentEncoding);
            response.setHeader(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
        }
        response.setHeader(HttpHeaders.CONTENT_TYPE, contentType);
        response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, public");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader(
                "Content-Security-Policy",
                "default-src 'self' blob: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; " +
                        "style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; " +
                        "connect-src 'self' blob: data:; worker-src 'self' blob:; media-src 'self' blob: data:; " +
                        "object-src 'none'; frame-ancestors " + frameAncestors()
        );
        response.setContentLengthLong(Files.size(assetFile));
        Files.copy(assetFile, response.getOutputStream());
    }

    private Path gameRoot() {
        return Paths.get(uploadStoragePath).toAbsolutePath().normalize().resolve("game").normalize();
    }

    private String frameAncestors() {
        String value = webglFrameAncestors == null ? "" : webglFrameAncestors.trim();
        if (value.isBlank() || "self".equalsIgnoreCase(value)) {
            return "'self'";
        }
        return value;
    }

    private Path resolveAssetFile(Path gameDir, String gameUuid, HttpServletRequest request) {
        String contextPath = request.getContextPath() == null ? "" : request.getContextPath();
        String requestUri = request.getRequestURI();
        String prefix = contextPath + "/game/" + gameUuid + "/";
        if (!requestUri.startsWith(prefix)) {
            return null;
        }

        String rawAssetPath = requestUri.substring(prefix.length());
        String assetPath = UriUtils.decode(rawAssetPath, StandardCharsets.UTF_8)
                .replace('\\', '/');
        while (assetPath.startsWith("/")) {
            assetPath = assetPath.substring(1);
        }
        if (assetPath.isBlank() || assetPath.contains("\0")) {
            return null;
        }

        Path assetFile = gameDir.resolve(assetPath).normalize();
        if (!assetFile.startsWith(gameDir)) {
            return null;
        }
        return assetFile;
    }

    private String normalizeGameUuid(String gameUuid) {
        if (gameUuid == null || gameUuid.isBlank()) {
            return null;
        }
        try {
            return UUID.fromString(gameUuid.trim()).toString();
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    private String contentEncoding(Path assetFile) {
        String fileName = assetFile.getFileName().toString().toLowerCase(Locale.ROOT);
        if (fileName.endsWith(".br")) {
            return "br";
        }
        if (fileName.endsWith(".gz")) {
            return "gzip";
        }
        return null;
    }

    private String contentType(Path assetFile) throws IOException {
        String fileName = assetFile.getFileName().toString();
        String effectiveName = uncompressedFileName(fileName).toLowerCase(Locale.ROOT);

        if (effectiveName.endsWith(".wasm")) {
            return "application/wasm";
        }
        if (effectiveName.endsWith(".js")) {
            return "application/javascript";
        }
        if (effectiveName.endsWith(".data")
                || effectiveName.endsWith(".mem")
                || effectiveName.endsWith(".symbols")) {
            return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        if (effectiveName.endsWith(".json")) {
            return MediaType.APPLICATION_JSON_VALUE;
        }
        if (effectiveName.endsWith(".html") || effectiveName.endsWith(".htm")) {
            return MediaType.TEXT_HTML_VALUE + ";charset=UTF-8";
        }
        if (effectiveName.endsWith(".css")) {
            return "text/css";
        }
        if (effectiveName.endsWith(".png")) {
            return MediaType.IMAGE_PNG_VALUE;
        }
        if (effectiveName.endsWith(".jpg") || effectiveName.endsWith(".jpeg")) {
            return MediaType.IMAGE_JPEG_VALUE;
        }
        if (effectiveName.endsWith(".gif")) {
            return MediaType.IMAGE_GIF_VALUE;
        }
        if (effectiveName.endsWith(".webp")) {
            return "image/webp";
        }
        if (effectiveName.endsWith(".svg")) {
            return "image/svg+xml";
        }

        String probedType = Files.probeContentType(assetFile);
        return probedType == null ? MediaType.APPLICATION_OCTET_STREAM_VALUE : probedType;
    }

    private String uncompressedFileName(String fileName) {
        String lowerName = fileName.toLowerCase(Locale.ROOT);
        if (lowerName.endsWith(".br") || lowerName.endsWith(".gz")) {
            int lastDot = fileName.lastIndexOf('.');
            return lastDot < 0 ? fileName : fileName.substring(0, lastDot);
        }
        return fileName;
    }
}
