package org.openautonomousconnection.webserver.runtime;

import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.protocol.side.server.CustomConnectedClient;
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.webserver.WebServer;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.WebHasher;
import org.openautonomousconnection.webserver.utils.RequestParams;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Dispatches Java WebPages using @Route annotation.
 */
public final class JavaPageDispatcher {

    private static final JavaRouteRegistry ROUTES = new JavaRouteRegistry();

    private JavaPageDispatcher() {}

    public static WebResponsePacket dispatch(
            CustomConnectedClient client,
            ProtocolWebServer server,
            WebRequestPacket request
    ) throws Exception {

        if (request.getPath() == null) return null;

        String route = request.getPath();
        if (!route.startsWith("/")) route = "/" + route;

        File contentRoot = server.getContentFolder();
        ROUTES.refreshIfNeeded(contentRoot);

        JavaRouteRegistry.RouteLookupResult found = ROUTES.find(route);

        // If no @Route match, still detect "requested a .java-backed path by filename"
        // (legacy behavior): if a matching *.java exists, compile/load but return error.
        if (found == null) {
            String p = route.startsWith("/") ? route.substring(1) : route;
            File javaFile = new File(contentRoot, p + ".java");
            if (javaFile.exists() && javaFile.isFile()) {
                // Compile/load but do not serve.
                new JavaPageCache().getOrCompile(javaFile);
                return error(501, "Java class exists but has no @Route: " + route);
            }
            return null; // not a Java route -> let file server handle it
        }

        // If it has @Route but is not a WebPage, compile/load but return error.
        if (!found.routable()) {
            return error(500, "Class has @Route but is not a WebPage: " + found.fqcn());
        }

        // Load and execute
        Class<?> clazz = Class.forName(found.fqcn());
        Object instance = clazz.getDeclaredConstructor().newInstance();

        if (!(instance instanceof WebPage page)) {
            return error(500, "Routed class is not a WebPage: " + found.fqcn());
        }

        WebHasher hasher = (server instanceof WebServer ws) ? ws.getHasher() : null;
        if (hasher == null) return error(500, "WebHasher missing on server instance.");

        WebPageContext ctx = new WebPageContext(client, server, request, new RequestParams(request), hasher);
        return page.handle(ctx);
    }

    private static WebResponsePacket error(int code, String msg) {
        return new WebResponsePacket(
                code,
                "text/plain; charset=utf-8",
                Map.of(),
                msg.getBytes(StandardCharsets.UTF_8)
        );
    }
}
