// Package apierror defines OpenAI-compatible typed errors for the Veylant proxy.
// All error responses follow the OpenAI JSON format so that existing OpenAI SDK
// clients can handle them without modification.
package apierror

import (
	"encoding/json"
	"net/http"
	"strconv"
)

// APIError represents an OpenAI-compatible error response body.
// Wire format: {"error":{"type":"...","message":"...","code":"..."}}
type APIError struct {
	Type          string `json:"type"`
	Message       string `json:"message"`
	Code          string `json:"code"`
	HTTPStatus    int    `json:"-"`
	RetryAfterSec int    `json:"-"` // when > 0, sets the Retry-After response header (RFC 6585)
}

// envelope wraps APIError in the OpenAI {"error": ...} envelope.
type envelope struct {
	Error *APIError `json:"error"`
}

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

// WriteError serialises e as JSON and writes it to w with the correct HTTP status.
// When e.RetryAfterSec > 0 it also sets the Retry-After header (RFC 6585).
func WriteError(w http.ResponseWriter, e *APIError) {
	if e.RetryAfterSec > 0 {
		w.Header().Set("Retry-After", strconv.Itoa(e.RetryAfterSec))
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(e.HTTPStatus)
	_ = json.NewEncoder(w).Encode(envelope{Error: e})
}

// WriteErrorWithRequestID is like WriteError but also echoes requestID in the
// X-Request-Id response header. Use this in middleware that has access to the
// request ID but where the header may not yet have been set by the RequestID
// middleware (e.g. when the request is short-circuited before reaching it).
func WriteErrorWithRequestID(w http.ResponseWriter, e *APIError, requestID string) {
	if requestID != "" {
		w.Header().Set("X-Request-Id", requestID)
	}
	WriteError(w, e)
}

// NewAuthError returns a 401 authentication_error.
func NewAuthError(msg string) *APIError {
	return &APIError{
		Type:       "authentication_error",
		Message:    msg,
		Code:       "invalid_api_key",
		HTTPStatus: http.StatusUnauthorized,
	}
}

// NewForbiddenError returns a 403 permission_error.
func NewForbiddenError(msg string) *APIError {
	return &APIError{
		Type:       "permission_error",
		Message:    msg,
		Code:       "insufficient_permissions",
		HTTPStatus: http.StatusForbidden,
	}
}

// NewBadRequestError returns a 400 invalid_request_error.
func NewBadRequestError(msg string) *APIError {
	return &APIError{
		Type:       "invalid_request_error",
		Message:    msg,
		Code:       "invalid_request",
		HTTPStatus: http.StatusBadRequest,
	}
}

// NewUpstreamError returns a 502 upstream_error.
func NewUpstreamError(msg string) *APIError {
	return &APIError{
		Type:       "api_error",
		Message:    msg,
		Code:       "upstream_error",
		HTTPStatus: http.StatusBadGateway,
	}
}

// NewRateLimitError returns a 429 rate_limit_error with Retry-After: 1 (RFC 6585).
func NewRateLimitError(msg string) *APIError {
	return &APIError{
		Type:          "rate_limit_error",
		Message:       msg,
		Code:          "rate_limit_exceeded",
		HTTPStatus:    http.StatusTooManyRequests,
		RetryAfterSec: 1,
	}
}

// NewTimeoutError returns a 504 timeout_error.
func NewTimeoutError(msg string) *APIError {
	return &APIError{
		Type:       "api_error",
		Message:    msg,
		Code:       "upstream_timeout",
		HTTPStatus: http.StatusGatewayTimeout,
	}
}

// NewInternalError returns a 500 internal_error.
func NewInternalError(msg string) *APIError {
	return &APIError{
		Type:       "api_error",
		Message:    msg,
		Code:       "internal_error",
		HTTPStatus: http.StatusInternalServerError,
	}
}
