package apierr

import (
	"fmt"
	"net/http"
)

// Error represents a structured API error
type Error struct {
	Code    string                 `json:"code"`    // PascalCase Error Code (e.g. "InvalidInput")
	Message string                 `json:"message"` // Human-readable message
	Details map[string]interface{} `json:"details,omitempty"`
	Status  int                    `json:"-"` // HTTP Status (not serialized)
	Cause   error                  `json:"-"` // Internal error cause (logged but not sent)

	permissionDenied bool
}

// Error implements the error interface
func (e *Error) Error() string {
	if e.Cause != nil {
		return fmt.Sprintf("%s: %v", e.Message, e.Cause)
	}
	return e.Message
}

// HTTPStatus exposes the already-owned transport status to boundary code that
// can classify generic API errors without importing this backend package. It is
// intentionally status-only; v63 wire code ownership stays at the API dialect.
func (e *Error) HTTPStatus() int {
	if e == nil {
		return 0
	}
	return e.Status
}

// Unwrap returns the internal cause
func (e *Error) Unwrap() error {
	return e.Cause
}

// New creates a new API Error
func New(status int, code string, msg string) *Error {
	return &Error{
		Status:  status,
		Code:    code,
		Message: msg,
	}
}

// WithDetails adds details to the error
func (e *Error) WithDetails(details map[string]interface{}) *Error {
	e.Details = details
	return e
}

// WithCause adds an internal cause to the error
func (e *Error) WithCause(err error) *Error {
	e.Cause = err
	return e
}

// Standard Error Constructors

func Invalid(msg string) *Error {
	return New(http.StatusBadRequest, "InvalidRequest", msg)
}

func NotFound(msg string) *Error {
	return New(http.StatusNotFound, "ResourceNotFound", msg)
}

func Unauthorized(msg string) *Error {
	return New(http.StatusUnauthorized, "Unauthorized", msg)
}

func Forbidden(msg string) *Error {
	return New(http.StatusForbidden, "Forbidden", msg)
}

// PermissionDenied preserves the established generic Forbidden transport
// representation while explicitly identifying the neutral authorization
// condition to internal engines. Callers must use it only when an authenticated
// principal lacks authority; Forbidden remains representation-only because a
// legacy 403 does not by itself prove that condition.
func PermissionDenied(msg string) *Error {
	err := Forbidden(msg)
	err.permissionDenied = true
	return err
}

// PermissionDeniedCondition lets app-blind internal engines classify this
// explicitly-authored condition without reading HTTP status or wire code.
func (e *Error) PermissionDeniedCondition() bool {
	return e != nil && e.permissionDenied
}

func Internal(err error) *Error {
	return New(http.StatusInternalServerError, "InternalServerError", "An unexpected error occurred").WithCause(err)
}

func Conflict(msg string) *Error {
	return New(http.StatusConflict, "ResourceConflict", msg)
}

func ValidationFailed(details map[string]interface{}) *Error {
	return New(http.StatusBadRequest, "ValidationFailed", "Validation failed").WithDetails(details)
}
