package ai.sentryagent.idp;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;

/**
 * Thrown for all API and network-level failures.
 * Extends RuntimeException — callers may catch if needed but are not required to.
 */
public final class AgentIdPException extends RuntimeException {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final String code;
    private final int httpStatus;
    private final Map<String, Object> details;

    public AgentIdPException(String code, String message, int httpStatus, Map<String, Object> details, Throwable cause) {
        super(message, cause);
        this.code       = code;
        this.httpStatus = httpStatus;
        this.details    = details;
    }

    public AgentIdPException(String code, String message, int httpStatus) {
        this(code, message, httpStatus, null, null);
    }

    /** Machine-readable error code (e.g. {@code "AgentNotFoundError"}). */
    public String getCode()                    { return code; }

    /** HTTP response status code, or 0 for network/build errors. */
    public int getHttpStatus()                 { return httpStatus; }

    /** Optional structured context from the API response. */
    public Map<String, Object> getDetails()    { return details; }

    // ─── Factory methods ──────────────────────────────────────────────────────

    /**
     * Creates an AgentIdPException from a raw JSON API error response body.
     * Falls back to UNKNOWN_ERROR if the body cannot be parsed.
     */
    public static AgentIdPException fromApiError(String responseBody, int httpStatus) {
        try {
            JsonNode node = MAPPER.readTree(responseBody);
            String code    = node.path("code").asText("UNKNOWN_ERROR");
            String message = node.path("message").asText("Unexpected HTTP " + httpStatus);
            if (code.isEmpty()) code = "UNKNOWN_ERROR";
            return new AgentIdPException(code, message, httpStatus);
        } catch (Exception e) {
            return new AgentIdPException("UNKNOWN_ERROR", "Unexpected HTTP " + httpStatus, httpStatus);
        }
    }

    /**
     * Creates an AgentIdPException from an OAuth 2.0 error response body.
     * Falls back to unknown_error if the body cannot be parsed.
     */
    public static AgentIdPException fromOAuth2Error(String responseBody, int httpStatus) {
        try {
            JsonNode node = MAPPER.readTree(responseBody);
            String code    = node.path("error").asText("unknown_error");
            String message = node.path("error_description").asText("Unexpected HTTP " + httpStatus);
            if (code.isEmpty()) code = "unknown_error";
            return new AgentIdPException(code, message, httpStatus);
        } catch (Exception e) {
            return new AgentIdPException("unknown_error", "Unexpected HTTP " + httpStatus, httpStatus);
        }
    }

    /** Creates an AgentIdPException wrapping a transport-level failure. */
    public static AgentIdPException networkError(Throwable cause) {
        return new AgentIdPException("NETWORK_ERROR", "Network error: " + cause.getMessage(), 0, null, cause);
    }

    @Override
    public String toString() {
        return "AgentIdPException{code='" + code + "', httpStatus=" + httpStatus + ", message='" + getMessage() + "'}";
    }
}
