package com.elphel.imagej.mcp;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.elphel.imagej.correction.Eyesis_Correction;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class McpServer {
    private static McpServer INSTANCE;

    private final Eyesis_Correction owner;
    private final int port;
    private HttpServer server;

    public static synchronized McpServer startIfNeeded(Eyesis_Correction owner, int port) {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        McpServer instance = new McpServer(owner, port);
        instance.start();
        INSTANCE = instance;
        return instance;
    }

    private McpServer(Eyesis_Correction owner, int port) {
        this.owner = owner;
        this.port = port;
    }

    private void start() {
        try {
            server = HttpServer.create(new InetSocketAddress("127.0.0.1", port), 0);
        } catch (IOException e) {
            System.out.println("MCP: failed to start HTTP server on port " + port + ": " + e.getMessage());
            return;
        }
        server.createContext("/mcp/status", new StatusHandler());
        server.createContext("/mcp/dialog", new DialogHandler());
        server.createContext("/mcp/dialog/values", new DialogValuesHandler());
        server.createContext("/mcp/button", new ButtonHandler());
        server.setExecutor(null);
        server.start();
        System.out.println("MCP: server started on http://127.0.0.1:" + port);
    }

    private class StatusHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            String response = buildStatusJson();
            sendJson(exchange, 200, response);
        }
    }

    private class DialogHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            String response = buildDialogJson();
            sendJson(exchange, 200, response);
        }
    }

    private class DialogValuesHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
                sendJson(exchange, 405, "{\"ok\":false,\"error\":\"POST required\"}");
                return;
            }
            Map<String, String> params = parseParams(exchange);
            String label = params.get("label");
            String value = params.get("value");
            if (label == null) {
                sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}");
                return;
            }
            McpDialogRegistry.setValue(label, value);
            sendJson(exchange, 200, "{\"ok\":true}");
        }
    }

    private class ButtonHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange exchange) throws IOException {
            if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
                sendJson(exchange, 405, "{\"ok\":false,\"error\":\"POST required\"}");
                return;
            }
            Map<String, String> params = parseParams(exchange);
            String label = params.get("label");
            if (label == null) {
                sendJson(exchange, 400, "{\"ok\":false,\"error\":\"Missing label\"}");
                return;
            }
            owner.triggerCommand(label);
            sendJson(exchange, 200, "{\"ok\":true}");
        }
    }

    private String buildStatusJson() {
        int stopRequested = owner.getSyncStopRequested();
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"running\":").append(owner.isSyncRunning());
        sb.append(",\"stopRequested\":").append(stopRequested);
        sb.append(",\"buttonLabel\":\"").append(jsonEscape(owner.getSyncButtonLabel())).append("\"");
        sb.append("}");
        return sb.toString();
    }

    private String buildDialogJson() {
        McpDialogSession session = McpDialogRegistry.getCurrent();
        if (session == null) {
            return "{\"ok\":true,\"dialog\":null}";
        }
        StringBuilder sb = new StringBuilder();
        sb.append("{\"ok\":true,\"dialog\":{");
        sb.append("\"id\":\"").append(jsonEscape(session.getId())).append("\",");
        sb.append("\"title\":\"").append(jsonEscape(session.getTitle())).append("\",");
        sb.append("\"fields\":[");
        List<McpDialogField> fields = session.getFields();
        for (int i = 0; i < fields.size(); i++) {
            McpDialogField f = fields.get(i);
            if (i > 0) {
                sb.append(",");
            }
            sb.append("{");
            sb.append("\"type\":\"").append(f.type.name().toLowerCase()).append("\"");
            if (f.label != null) {
                sb.append(",\"label\":\"").append(jsonEscape(f.label)).append("\"");
            }
            if (f.tab != null) {
                sb.append(",\"tab\":\"").append(jsonEscape(f.tab)).append("\"");
            }
            if (f.tooltip != null) {
                sb.append(",\"tooltip\":\"").append(jsonEscape(f.tooltip)).append("\"");
            }
            if (f.units != null) {
                sb.append(",\"units\":\"").append(jsonEscape(f.units)).append("\"");
            }
            if (f.defaultValue != null) {
                sb.append(",\"default\":\"").append(jsonEscape(f.defaultValue)).append("\"");
            }
            if (f.choices != null) {
                sb.append(",\"choices\":[");
                for (int c = 0; c < f.choices.length; c++) {
                    if (c > 0) {
                        sb.append(",");
                    }
                    sb.append("\"").append(jsonEscape(f.choices[c])).append("\"");
                }
                sb.append("]");
            }
            sb.append("}");
        }
        sb.append("]}}" );
        return sb.toString();
    }

    private static void sendJson(HttpExchange exchange, int code, String body) throws IOException {
        byte[] data = body.getBytes(StandardCharsets.UTF_8);
        Headers headers = exchange.getResponseHeaders();
        headers.set("Content-Type", "application/json; charset=utf-8");
        exchange.sendResponseHeaders(code, data.length);
        OutputStream os = exchange.getResponseBody();
        os.write(data);
        os.close();
    }

    private static Map<String, String> parseParams(HttpExchange exchange) throws IOException {
        Map<String, String> params = new HashMap<String, String>();
        URI uri = exchange.getRequestURI();
        if (uri != null && uri.getQuery() != null) {
            parseQueryString(uri.getQuery(), params);
        }
        if ("POST".equalsIgnoreCase(exchange.getRequestMethod())) {
            byte[] body = exchange.getRequestBody().readAllBytes();
            if (body.length > 0) {
                String raw = new String(body, StandardCharsets.UTF_8);
                parseQueryString(raw, params);
            }
        }
        return params;
    }

    private static void parseQueryString(String raw, Map<String, String> out) {
        if (raw == null || raw.isEmpty()) {
            return;
        }
        String[] pairs = raw.split("&");
        for (String pair : pairs) {
            if (pair.isEmpty()) {
                continue;
            }
            int idx = pair.indexOf('=');
            String key = idx >= 0 ? pair.substring(0, idx) : pair;
            String value = idx >= 0 ? pair.substring(idx + 1) : "";
            out.put(urlDecode(key), urlDecode(value));
        }
    }

    private static String urlDecode(String value) {
        try {
            return URLDecoder.decode(value, StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            return value;
        }
    }

    private static String jsonEscape(String value) {
        if (value == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder(value.length() + 10);
        for (int i = 0; i < value.length(); i++) {
            char c = value.charAt(i);
            switch (c) {
                case '\\': sb.append("\\\\"); break;
                case '"': sb.append("\\\""); break;
                case '\n': sb.append("\\n"); break;
                case '\r': sb.append("\\r"); break;
                case '\t': sb.append("\\t"); break;
                default: sb.append(c); break;
            }
        }
        return sb.toString();
    }
}
