package server.debug;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.api.Session;
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
import server.logic.ws_protocol.JSON.ConnectionContext;
import shine.db.dao.ActiveSessionsDAO;
import shine.db.entities.ActiveSessionEntry;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

/**
 * GET /debug/ws/clients
 */
public class DebugClientsServlet extends HttpServlet {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final DebugTokenProvider tokenProvider;

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

    @Override
    protected void doGet(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;
        }

        Set<ConnectionContext> contexts = ActiveConnectionsRegistry.getInstance().getAllConnectionsSnapshot();
        List<ObjectNode> rows = new ArrayList<>();
        for (ConnectionContext ctx : contexts) {
            Session ws = ctx.getWsSession();
            if (ws == null || !ws.isOpen()) continue;

            ObjectNode row = MAPPER.createObjectNode();
            String sessionId = safe(ctx.getSessionId());
            row.put("sessionId", sessionId);
            row.put("login", safe(ctx.getLogin()));
            row.put("authStatus", ctx.getAuthenticationStatus());
            row.put("wsOpen", ws.isOpen());

            String remote = safeRemote(ws.getRemoteAddress());
            row.put("remoteAddress", remote);
            row.put("ip", extractIp(ws.getRemoteAddress()));
            row.put("userAgent", safeUserAgent(ws));

            ActiveSessionEntry active = null;
            try {
                if (!sessionId.isBlank()) {
                    active = ActiveSessionsDAO.getInstance().getBySessionId(sessionId);
                }
            } catch (Exception ignored) {
            }

            row.put("clientInfoFromClient", active != null ? safe(active.getClientInfoFromClient()) : "");
            row.put("clientInfoFromRequest", active != null ? safe(active.getClientInfoFromRequest()) : "");
            row.put("userLanguage", active != null ? safe(active.getUserLanguage()) : "");
            row.put("sessionCreatedAtMs", active != null ? active.getSessionCreatedAtMs() : 0L);

            rows.add(row);
        }

        rows.sort(Comparator.comparing(a -> a.path("sessionId").asText("")));

        ArrayNode clients = MAPPER.createArrayNode();
        rows.forEach(clients::add);

        ObjectNode payload = MAPPER.createObjectNode();
        payload.put("count", clients.size());
        payload.set("clients", clients);

        writeOk(resp, payload);
    }

    private static String safeUserAgent(Session ws) {
        try {
            if (ws.getUpgradeRequest() == null) return "";
            return safe(ws.getUpgradeRequest().getHeader("User-Agent"));
        } catch (Throwable ignored) {
            return "";
        }
    }

    private static String extractIp(SocketAddress addr) {
        if (addr instanceof InetSocketAddress inet) {
            if (inet.getAddress() != null) {
                return safe(inet.getAddress().getHostAddress());
            }
            return safe(inet.getHostString());
        }
        return "";
    }

    private static String safeRemote(SocketAddress addr) {
        return addr == null ? "" : safe(addr.toString());
    }

    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));
    }
}
