package com.banesco.common.infrastructure.filter;

import com.banesco.common.infrastructure.context.RequestContext;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Slf4j
@Provider
public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) {
        String requestId = requestContext.getHeaderString(RequestContext.DEVICE_SESSION_REFERENCE);

        if (isEmpty(requestId)) {
            requestId = requestContext.getUriInfo()
                .getQueryParameters()
                .getFirst(RequestContext.DEVICE_SESSION_REFERENCE);
        }

        if (isEmpty(requestId) && hasJsonBody(requestContext)) {
            requestId = extractRequestIdFromBody(requestContext);
        }

        if (isEmpty(requestId)) {
            requestId = UUID.randomUUID().toString().substring(0, 13);
        }

        RequestContext.setRequestId(requestId);
    }

    private boolean isEmpty(String value) {
        return value == null || value.trim().isEmpty();
    }

    private boolean hasJsonBody(ContainerRequestContext context) {
        try {
            String method = context.getMethod();
            String contentType = context.getHeaderString("Content-Type");

            return ("POST".equals(method) || "PUT".equals(method))
                && contentType != null
                && contentType.contains("application/json");
        } catch (Exception e) {
            log.warn("La peticion no es un POST o PUT: {}", e.getMessage());
            return false;
        }
    }

    private String extractRequestIdFromBody(ContainerRequestContext context) {
        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            context.getEntityStream().transferTo(buffer);
            byte[] bodyBytes = buffer.toByteArray();

            context.setEntityStream(new ByteArrayInputStream(bodyBytes));

            String bodyString = new String(bodyBytes, StandardCharsets.UTF_8);
            io.vertx.core.json.JsonObject jsonObject = new io.vertx.core.json.JsonObject(bodyString);

            if (jsonObject.containsKey(RequestContext.DEVICE)) {
                io.vertx.core.json.JsonObject device = jsonObject.getJsonObject(RequestContext.DEVICE);

                if (device.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
                    return device.getString(RequestContext.DEVICE_SESSION_REFERENCE);
                }
            }

            if (jsonObject.containsKey(RequestContext.REQUEST_ID)) {
                return jsonObject.getString(RequestContext.REQUEST_ID);
            }

            if (jsonObject.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
                return jsonObject.getString(RequestContext.DEVICE_SESSION_REFERENCE);
            }

            return null;
        } catch (Exception e) {
            log.error("Error extrayendo el requestId del cuerpo de la peticion: {}", e.getMessage());
            return null;
        }
    }

    @Override
    public void filter(
        ContainerRequestContext requestContext,
        ContainerResponseContext responseContext
    ) {
        try {
            RequestContext.clear();
        } catch (Exception e) {
            log.error("Error limpiando el filtro: {}", e.getMessage());
        }
    }
}