package ai.sentryagent.idp.internal;

import ai.sentryagent.idp.AgentIdPException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Shared HTTP helper for all service clients.
 * Handles JSON serialization, Authorization header injection, and error mapping.
 */
public final class HttpHelper {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final HttpClient httpClient;

    public HttpHelper(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    /**
     * Performs a synchronous JSON request and unmarshals the response into {@code responseType}.
     * Returns null for 204 No Content responses.
     *
     * @throws AgentIdPException on HTTP errors or network failures
     */
    public <T> T request(String method, String url, Object body, String token, Class<T> responseType) {
        try {
            HttpRequest req = buildRequest(method, url, body, token);
            HttpResponse<String> resp = httpClient.send(req, HttpResponse.BodyHandlers.ofString());
            return handleResponse(resp, responseType);
        } catch (AgentIdPException e) {
            throw e;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw AgentIdPException.networkError(e);
        } catch (IOException e) {
            throw AgentIdPException.networkError(e);
        }
    }

    /**
     * Performs an asynchronous JSON request and returns a CompletableFuture.
     *
     * @throws AgentIdPException (wrapped in CompletableFuture) on HTTP errors
     */
    public <T> CompletableFuture<T> requestAsync(String method, String url, Object body, String token, Class<T> responseType) {
        try {
            HttpRequest req = buildRequest(method, url, body, token);
            return httpClient.sendAsync(req, HttpResponse.BodyHandlers.ofString())
                .thenApply(resp -> handleResponse(resp, responseType));
        } catch (Exception e) {
            return CompletableFuture.failedFuture(AgentIdPException.networkError(e));
        }
    }

    private HttpRequest buildRequest(String method, String url, Object body, String token) throws IOException {
        HttpRequest.BodyPublisher publisher = body != null
            ? HttpRequest.BodyPublishers.ofString(MAPPER.writeValueAsString(body))
            : HttpRequest.BodyPublishers.noBody();

        HttpRequest.Builder builder = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .method(method, publisher)
            .header("Accept", "application/json");

        if (body != null) {
            builder.header("Content-Type", "application/json");
        }
        if (token != null && !token.isEmpty()) {
            builder.header("Authorization", "Bearer " + token);
        }

        return builder.build();
    }

    private <T> T handleResponse(HttpResponse<String> resp, Class<T> responseType) {
        int status = resp.statusCode();
        if (status < 200 || status >= 300) {
            throw AgentIdPException.fromApiError(resp.body(), status);
        }
        if (status == 204 || responseType == Void.class) {
            return null;
        }
        try {
            return MAPPER.readValue(resp.body(), responseType);
        } catch (IOException e) {
            throw new AgentIdPException("PARSE_ERROR", "Failed to parse response: " + e.getMessage(), status);
        }
    }
}
