package server.logic.ws_protocol.JSON.handlers.system;

import org.eclipse.jetty.websocket.api.Session;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_CallDeliveryReport_Request;
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_CallDeliveryReport_Response;
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
import server.logic.ws_protocol.WireCodes;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;

public class Net_CallDeliveryReport_Handler implements JsonMessageHandler {
    private static final ReentrantLock FILE_LOCK = new ReentrantLock(true);
    private static final Path LOG_FILE = Path.of("logs", "call-delivery-events.log");

    @Override
    public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
        Net_CallDeliveryReport_Request req = (Net_CallDeliveryReport_Request) baseRequest;
        if (safe(req.getType()).isEmpty()) {
            return NetExceptionResponseFactory.error(
                    req, WireCodes.Status.BAD_REQUEST, "BAD_FIELDS", "Поле type обязательно"
            );
        }

        long serverTs = System.currentTimeMillis();
        String line = String.format(
                Locale.ROOT,
                "%s | type=%s | op=%s | requestId=%s | login=%s | sessionId=%s | authenticated=%s | remote=%s | userAgent=%s | value=%s%n",
                Instant.ofEpochMilli(serverTs),
                clip(req.getType(), 80),
                clip(req.getOp(), 80),
                clip(req.getRequestId(), 120),
                clip(ctx != null ? ctx.getLogin() : "", 80),
                clip(ctx != null ? ctx.getSessionId() : "", 160),
                ctx != null && ctx.isAuthenticatedUser(),
                clip(remoteAddress(ctx), 200),
                clip(userAgent(ctx), 300),
                clip(req.getValue(), 8000)
        );

        appendLine(line);

        Net_CallDeliveryReport_Response resp = new Net_CallDeliveryReport_Response();
        resp.setOp(req.getOp());
        resp.setRequestId(req.getRequestId());
        resp.setStatus(WireCodes.Status.OK);
        resp.setAccepted(true);
        resp.setServerTs(serverTs);
        return resp;
    }

    private static void appendLine(String line) {
        FILE_LOCK.lock();
        try {
            Path parent = LOG_FILE.getParent();
            if (parent != null) {
                Files.createDirectories(parent);
            }
            Files.writeString(
                    LOG_FILE,
                    line,
                    StandardCharsets.UTF_8,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.APPEND
            );
        } catch (IOException e) {
            throw new IllegalStateException("Не удалось записать call-delivery-report", e);
        } finally {
            FILE_LOCK.unlock();
        }
    }

    private static String remoteAddress(ConnectionContext ctx) {
        if (ctx == null) return "";
        Session ws = ctx.getWsSession();
        if (ws == null) return "";
        SocketAddress remote = ws.getRemoteAddress();
        return remote != null ? remote.toString() : "";
    }

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

    private static String userAgent(ConnectionContext ctx) {
        if (ctx == null) return "";
        Session ws = ctx.getWsSession();
        if (ws == null) return "";
        try {
            String value = ws.getUpgradeRequest() != null ? ws.getUpgradeRequest().getHeader("User-Agent") : "";
            return safe(value);
        } catch (Exception ignored) {
            return "";
        }
    }

    private static String clip(String value, int maxLen) {
        String cleaned = safe(value).replace('\n', ' ').replace('\r', ' ');
        if (cleaned.length() <= maxLen) return cleaned;
        return cleaned.substring(0, Math.max(0, maxLen - 3)) + "...";
    }
}
