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.debug.DebugRunLogBuffer;
import server.logic.ws_protocol.JSON.push.WsEventSender;

import java.io.IOException;

/**
 * POST /debug/ws/connect
 */
public class DebugConnectServlet extends HttpServlet {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final DebugTokenProvider tokenProvider;

    public DebugConnectServlet(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;
        }
        if (!tokenProvider.matchesBearerHeader(req.getHeader("Authorization"))) {
            writeError(resp, 401, "UNAUTHORIZED", "Неверный Bearer token");
            return;
        }

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

        String initiatorSessionId = text(body, "initiatorSessionId");
        String responderSessionId = text(body, "responderSessionId");
        boolean clearDebugLog = body != null && body.path("clearDebugLog").asBoolean(false);

        if (initiatorSessionId.isBlank() || responderSessionId.isBlank()) {
            writeError(resp, 400, "BAD_FIELDS", "initiatorSessionId и responderSessionId обязательны");
            return;
        }
        if (initiatorSessionId.equals(responderSessionId)) {
            writeError(resp, 400, "BAD_FIELDS", "Нужны две разные sessionId");
            return;
        }

        ConnectionContext initiator = ActiveConnectionsRegistry.getInstance().getBySessionId(initiatorSessionId);
        ConnectionContext responder = ActiveConnectionsRegistry.getInstance().getBySessionId(responderSessionId);

        if (initiator == null || initiator.getWsSession() == null || !initiator.getWsSession().isOpen()) {
            writeError(resp, 404, "INITIATOR_NOT_FOUND", "Сессия initiator не найдена или неактивна");
            return;
        }
        if (responder == null || responder.getWsSession() == null || !responder.getWsSession().isOpen()) {
            writeError(resp, 404, "RESPONDER_NOT_FOUND", "Сессия responder не найдена или неактивна");
            return;
        }

        DebugRunLogBuffer logs = DebugRunLogBuffer.getInstance();
        if (clearDebugLog) {
            logs.clear();
        }
        String runId = logs.newRunId();
        String callId = "debug-call-" + System.currentTimeMillis();

        ObjectNode responderPayload = MAPPER.createObjectNode();
        responderPayload.put("runId", runId);
        responderPayload.put("callId", callId);
        responderPayload.put("peerSessionId", initiatorSessionId);
        responderPayload.put("peerLogin", safe(initiator.getLogin()));
        responderPayload.put("role", "responder");
        responderPayload.put("issuedAtMs", System.currentTimeMillis());

        ObjectNode initiatorPayload = MAPPER.createObjectNode();
        initiatorPayload.put("runId", runId);
        initiatorPayload.put("callId", callId);
        initiatorPayload.put("peerSessionId", responderSessionId);
        initiatorPayload.put("peerLogin", safe(responder.getLogin()));
        initiatorPayload.put("role", "initiator");
        initiatorPayload.put("issuedAtMs", System.currentTimeMillis());

        boolean responderOk = WsEventSender.sendEvent(responder, "DebugConnectPrepareResponder", "evt-" + callId + "-r", responderPayload);
        boolean initiatorOk = WsEventSender.sendEvent(initiator, "DebugConnectStartInitiator", "evt-" + callId + "-i", initiatorPayload);

        logs.add("info", runId, "debug-connect", initiatorSessionId, safe(initiator.getLogin()),
                "Запуск debug-run", "initiator->" + responderSessionId + ", sentResponder=" + responderOk + ", sentInitiator=" + initiatorOk);

        ObjectNode payload = MAPPER.createObjectNode();
        payload.put("runId", runId);
        payload.put("callId", callId);
        payload.put("accepted", responderOk && initiatorOk);
        payload.put("initiatorSessionId", initiatorSessionId);
        payload.put("responderSessionId", responderSessionId);
        payload.put("initiatorLogin", safe(initiator.getLogin()));
        payload.put("responderLogin", safe(responder.getLogin()));
        if (!safe(initiator.getLogin()).equalsIgnoreCase(safe(responder.getLogin()))) {
            payload.put("mode", "cross-login");
        } else {
            payload.put("warning", "Сессии принадлежат одному login, рекомендован тест между разными login");
        }

        writeOk(resp, payload);
    }

    private static String text(JsonNode node, String field) {
        if (node == null) return "";
        return safe(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));
    }
}
