package server.debug;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.push.WsEventSender;

import java.io.IOException;
import java.util.Set;

/**
 * POST /debug/ws/ui-reload-all
 *
 * Заголовки:
 * - X-Debug-Token: <token> (рекомендуется)
 * - или Authorization: Bearer <token>
 *
 * Тело (опционально):
 * {
 *   "reason": "deploy_ui",
 *   "reloadAfterMs": 800
 * }
 */
public class DebugUiReloadAllServlet extends HttpServlet {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final DebugTokenProvider tokenProvider;

    public DebugUiReloadAllServlet(DebugTokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (!tokenProvider.isEnabled()) {
            writeError(resp, 503, "DEBUG_DISABLED", "Debug API отключен: .debug-token не найден или пуст");
            return;
        }

        String headerToken = req.getHeader("X-Debug-Token");
        String bearerHeader = req.getHeader("Authorization");
        boolean authorized = tokenProvider.matchesRawToken(headerToken) || tokenProvider.matchesBearerHeader(bearerHeader);
        if (!authorized) {
            writeError(resp, 401, "UNAUTHORIZED", "Неверный debug token");
            return;
        }

        JsonNode body = null;
        try {
            if (req.getContentLengthLong() != 0) {
                body = MAPPER.readTree(req.getInputStream());
            }
        } catch (Exception e) {
            writeError(resp, 400, "BAD_JSON", "Тело запроса должно быть JSON");
            return;
        }

        String reason = safe(text(body, "reason"));
        if (reason.isBlank()) reason = "manual_debug_api";
        int reloadAfterMs = clampInt(body == null ? 700 : body.path("reloadAfterMs").asInt(700), 100, 15_000);

        long issuedAtMs = System.currentTimeMillis();
        Set<ConnectionContext> all = ActiveConnectionsRegistry.getInstance().getAllConnectionsSnapshot();
        int totalConnections = 0;
        int sent = 0;

        for (ConnectionContext ctx : all) {
            if (ctx == null || ctx.getWsSession() == null || !ctx.getWsSession().isOpen()) continue;
            totalConnections += 1;

            ObjectNode payload = MAPPER.createObjectNode();
            payload.put("reason", reason);
            payload.put("issuedAtMs", issuedAtMs);
            payload.put("reloadAfterMs", reloadAfterMs);

            String sessionId = safe(ctx.getSessionId());
            String eventId = "evt-ui-reload-" + issuedAtMs + "-" + (sessionId.isBlank() ? totalConnections : sessionId);
            if (WsEventSender.sendEvent(ctx, "ForceUiReload", eventId, payload)) {
                sent += 1;
            }
        }

        ObjectNode payload = MAPPER.createObjectNode();
        payload.put("accepted", sent > 0);
        payload.put("reason", reason);
        payload.put("reloadAfterMs", reloadAfterMs);
        payload.put("issuedAtMs", issuedAtMs);
        payload.put("totalConnections", totalConnections);
        payload.put("sentCount", sent);
        payload.put("skippedCount", Math.max(0, totalConnections - sent));
        writeOk(resp, payload);
    }

    private static int clampInt(int value, int min, int max) {
        return Math.min(max, Math.max(min, value));
    }

    private static String text(JsonNode node, String field) {
        if (node == null) return "";
        return String.valueOf(node.path(field).asText(""));
    }

    private static String safe(String value) {
        return value == null ? "" : value.trim();
    }

    private static void writeOk(HttpServletResponse resp, ObjectNode payload) throws IOException {
        ObjectNode root = MAPPER.createObjectNode();
        root.put("ok", true);
        root.set("payload", payload == null ? MAPPER.createObjectNode() : payload);
        writeJson(resp, 200, root);
    }

    private static void writeError(HttpServletResponse resp, int status, String code, String message) throws IOException {
        ObjectNode root = MAPPER.createObjectNode();
        root.put("ok", false);
        root.put("code", code);
        root.put("message", message);
        writeJson(resp, status, root);
    }

    private static void writeJson(HttpServletResponse resp, int status, ObjectNode root) throws IOException {
        resp.setStatus(status);
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json; charset=UTF-8");
        resp.getWriter().write(MAPPER.writeValueAsString(root));
    }
}
