package agentidp

import (
	"encoding/json"
	"fmt"
)

// AgentIdPError is returned for all API and network failures.
// It implements the error interface.
type AgentIdPError struct {
	// Code is a machine-readable error code (e.g. "AgentNotFoundError").
	Code string
	// Message is a human-readable description.
	Message string
	// HTTPStatus is the HTTP response status code, or 0 for network errors.
	HTTPStatus int
	// Details contains additional structured context, if provided by the API.
	Details map[string]interface{}
}

// Error implements the error interface.
func (e *AgentIdPError) Error() string {
	return e.Message
}

// apiErrorBody is the standard JSON error body from the AgentIdP REST API.
type apiErrorBody struct {
	Code    string                 `json:"code"`
	Message string                 `json:"message"`
	Details map[string]interface{} `json:"details,omitempty"`
}

// oauth2ErrorBody is the standard JSON error body from OAuth 2.0 token endpoints.
type oauth2ErrorBody struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

// parseAPIError attempts to unmarshal a JSON response body into an AgentIdPError.
// Falls back to a generic UNKNOWN_ERROR if the body cannot be parsed.
func parseAPIError(body []byte, status int) *AgentIdPError {
	var apiErr apiErrorBody
	if err := json.Unmarshal(body, &apiErr); err == nil && apiErr.Code != "" {
		return &AgentIdPError{
			Code:       apiErr.Code,
			Message:    apiErr.Message,
			HTTPStatus: status,
			Details:    apiErr.Details,
		}
	}
	return &AgentIdPError{
		Code:       "UNKNOWN_ERROR",
		Message:    fmt.Sprintf("unexpected HTTP %d", status),
		HTTPStatus: status,
	}
}

// parseOAuth2Error attempts to unmarshal a JSON response body into an AgentIdPError
// using the OAuth 2.0 error format. Falls back to UNKNOWN_ERROR on parse failure.
func parseOAuth2Error(body []byte, status int) *AgentIdPError {
	var oauthErr oauth2ErrorBody
	if err := json.Unmarshal(body, &oauthErr); err == nil && oauthErr.Error != "" {
		return &AgentIdPError{
			Code:       oauthErr.Error,
			Message:    oauthErr.ErrorDescription,
			HTTPStatus: status,
		}
	}
	return &AgentIdPError{
		Code:       "UNKNOWN_ERROR",
		Message:    fmt.Sprintf("unexpected HTTP %d", status),
		HTTPStatus: status,
	}
}

// newNetworkError creates an AgentIdPError for transport-level failures.
func newNetworkError(cause error) *AgentIdPError {
	return &AgentIdPError{
		Code:       "NETWORK_ERROR",
		Message:    fmt.Sprintf("network error: %s", cause.Error()),
		HTTPStatus: 0,
	}
}
