// Package ucxl_codes provides standardized UCXL error and response codes
// for consistent cross-service communication and client handling.
//
// Based on UCXL-ERROR-CODES.md and UCXL-RESPONSE-CODES.md v1.0
package ucxl_codes

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
)

// UCXLErrorCode represents standard UCXL error codes following format: UCXL-<HTTP-class>-<SHORT_NAME>
type UCXLErrorCode string

const (
	// 400 - Client Errors
	ErrorInvalidAddress     UCXLErrorCode = "UCXL-400-INVALID_ADDRESS"
	ErrorMissingField      UCXLErrorCode = "UCXL-400-MISSING_FIELD"
	ErrorInvalidFormat     UCXLErrorCode = "UCXL-400-INVALID_FORMAT"
	
	// 401 - Authentication
	ErrorUnauthorized      UCXLErrorCode = "UCXL-401-UNAUTHORIZED"
	
	// 403 - Authorization
	ErrorForbidden         UCXLErrorCode = "UCXL-403-FORBIDDEN"
	
	// 404 - Not Found
	ErrorNotFound          UCXLErrorCode = "UCXL-404-NOT_FOUND"
	
	// 409 - Conflict
	ErrorConflict          UCXLErrorCode = "UCXL-409-CONFLICT"
	
	// 422 - Unprocessable Entity
	ErrorUnprocessableEntity UCXLErrorCode = "UCXL-422-UNPROCESSABLE_ENTITY"
	
	// 429 - Rate Limiting
	ErrorRateLimit         UCXLErrorCode = "UCXL-429-RATE_LIMIT"
	
	// 500 - Server Errors
	ErrorInternalError     UCXLErrorCode = "UCXL-500-INTERNAL_ERROR"
	
	// 503 - Service Unavailable
	ErrorServiceUnavailable UCXLErrorCode = "UCXL-503-SERVICE_UNAVAILABLE"
	
	// 504 - Gateway Timeout
	ErrorGatewayTimeout    UCXLErrorCode = "UCXL-504-GATEWAY_TIMEOUT"
)

// UCXLResponseCode represents standard UCXL response codes following format: UCXL-<HTTP-class>-<SHORT_NAME>
type UCXLResponseCode string

const (
	// 200 - Success
	ResponseOK             UCXLResponseCode = "UCXL-200-OK"
	
	// 201 - Created
	ResponseCreated        UCXLResponseCode = "UCXL-201-CREATED"
	
	// 202 - Accepted (Async)
	ResponseAccepted       UCXLResponseCode = "UCXL-202-ACCEPTED"
	
	// 204 - No Content
	ResponseNoContent      UCXLResponseCode = "UCXL-204-NO_CONTENT"
	
	// 206 - Partial Content
	ResponsePartialContent UCXLResponseCode = "UCXL-206-PARTIAL_CONTENT"
	
	// 304 - Not Modified (Caching)
	ResponseNotModified    UCXLResponseCode = "UCXL-304-NOT_MODIFIED"
)

// HTTPStatus returns the HTTP status code for the error code
func (e UCXLErrorCode) HTTPStatus() int {
	switch e {
	case ErrorInvalidAddress, ErrorMissingField, ErrorInvalidFormat:
		return 400
	case ErrorUnauthorized:
		return 401
	case ErrorForbidden:
		return 403
	case ErrorNotFound:
		return 404
	case ErrorConflict:
		return 409
	case ErrorUnprocessableEntity:
		return 422
	case ErrorRateLimit:
		return 429
	case ErrorInternalError:
		return 500
	case ErrorServiceUnavailable:
		return 503
	case ErrorGatewayTimeout:
		return 504
	default:
		return 500
	}
}

// DefaultMessage returns the default error message for this code
func (e UCXLErrorCode) DefaultMessage() string {
	switch e {
	case ErrorInvalidAddress:
		return "Invalid UCXL address format"
	case ErrorMissingField:
		return "Required field is missing"
	case ErrorInvalidFormat:
		return "Input does not match the expected format"
	case ErrorUnauthorized:
		return "Authentication credentials missing or invalid"
	case ErrorForbidden:
		return "Insufficient permissions for the action"
	case ErrorNotFound:
		return "Requested resource not found"
	case ErrorConflict:
		return "Conflict with current state"
	case ErrorUnprocessableEntity:
		return "Semantic validation failed"
	case ErrorRateLimit:
		return "Too many requests; rate limiting in effect"
	case ErrorInternalError:
		return "Internal server error"
	case ErrorServiceUnavailable:
		return "Service is currently unavailable"
	case ErrorGatewayTimeout:
		return "Downstream gateway timed out"
	default:
		return "Unknown error"
	}
}

// IsClientError checks if this is a client error (4xx)
func (e UCXLErrorCode) IsClientError() bool {
	status := e.HTTPStatus()
	return status >= 400 && status < 500
}

// IsServerError checks if this is a server error (5xx)
func (e UCXLErrorCode) IsServerError() bool {
	status := e.HTTPStatus()
	return status >= 500 && status < 600
}

// ShouldRetry checks if this error should trigger a retry
func (e UCXLErrorCode) ShouldRetry() bool {
	switch e {
	case ErrorRateLimit, ErrorInternalError, ErrorServiceUnavailable, ErrorGatewayTimeout:
		return true
	default:
		return false
	}
}

// HTTPStatus returns the HTTP status code for the response code
func (r UCXLResponseCode) HTTPStatus() int {
	switch r {
	case ResponseOK:
		return 200
	case ResponseCreated:
		return 201
	case ResponseAccepted:
		return 202
	case ResponseNoContent:
		return 204
	case ResponsePartialContent:
		return 206
	case ResponseNotModified:
		return 304
	default:
		return 200
	}
}

// DefaultMessage returns the default success message for this code
func (r UCXLResponseCode) DefaultMessage() string {
	switch r {
	case ResponseOK:
		return "Request completed successfully"
	case ResponseCreated:
		return "Resource created successfully"
	case ResponseAccepted:
		return "Request accepted for processing"
	case ResponseNoContent:
		return "Request completed with no content to return"
	case ResponsePartialContent:
		return "Partial results returned"
	case ResponseNotModified:
		return "Resource not modified since last fetch"
	default:
		return "Success"
	}
}

// IsAsync checks if this indicates an asynchronous operation
func (r UCXLResponseCode) IsAsync() bool {
	return r == ResponseAccepted
}

// IsPartial checks if this indicates partial/incomplete results
func (r UCXLResponseCode) IsPartial() bool {
	return r == ResponsePartialContent || r == ResponseAccepted
}

// UCXLErrorResponse represents the standardized UCXL error response payload
type UCXLErrorResponse struct {
	Error UCXLError `json:"error"`
}

// UCXLError contains the error details
type UCXLError struct {
	Code      UCXLErrorCode          `json:"code"`
	Message   string                 `json:"message"`
	Details   map[string]interface{} `json:"details,omitempty"`
	Source    string                 `json:"source"`
	Path      string                 `json:"path"`
	RequestID string                 `json:"request_id"`
	Timestamp time.Time              `json:"timestamp"`
	Cause     string                 `json:"cause,omitempty"`
}

// UCXLSuccessResponse represents the standardized UCXL success response payload
type UCXLSuccessResponse struct {
	Response UCXLResponse `json:"response"`
}

// UCXLResponse contains the response details
type UCXLResponse struct {
	Code      UCXLResponseCode       `json:"code"`
	Message   string                 `json:"message"`
	Data      interface{}            `json:"data,omitempty"`
	Details   map[string]interface{} `json:"details,omitempty"`
	RequestID string                 `json:"request_id"`
	Timestamp time.Time              `json:"timestamp"`
}

// ErrorBuilder helps build standardized error responses
type ErrorBuilder struct {
	code      UCXLErrorCode
	message   string
	details   map[string]interface{}
	source    string
	path      string
	requestID string
	cause     string
}

// NewErrorBuilder creates a new error builder with the specified code
func NewErrorBuilder(code UCXLErrorCode) *ErrorBuilder {
	return &ErrorBuilder{
		code:      code,
		details:   make(map[string]interface{}),
		source:    "ucxl-api/v1",
		path:      "/",
		requestID: fmt.Sprintf("req-%s", uuid.New().String()[:8]),
	}
}

// Message sets a custom error message
func (b *ErrorBuilder) Message(message string) *ErrorBuilder {
	b.message = message
	return b
}

// Field adds details about the field that failed
func (b *ErrorBuilder) Field(field string, provided interface{}) *ErrorBuilder {
	b.details["field"] = field
	b.details["provided"] = provided
	return b
}

// ExpectedFormat adds expected format information
func (b *ErrorBuilder) ExpectedFormat(format string) *ErrorBuilder {
	b.details["expected_format"] = format
	return b
}

// Detail adds a detail field
func (b *ErrorBuilder) Detail(key string, value interface{}) *ErrorBuilder {
	b.details[key] = value
	return b
}

// Source sets the source service
func (b *ErrorBuilder) Source(source string) *ErrorBuilder {
	b.source = source
	return b
}

// Path sets the request path
func (b *ErrorBuilder) Path(path string) *ErrorBuilder {
	b.path = path
	return b
}

// RequestID sets the request ID
func (b *ErrorBuilder) RequestID(requestID string) *ErrorBuilder {
	b.requestID = requestID
	return b
}

// Cause sets the error cause
func (b *ErrorBuilder) Cause(cause string) *ErrorBuilder {
	b.cause = cause
	return b
}

// Build creates the error response
func (b *ErrorBuilder) Build() *UCXLErrorResponse {
	message := b.message
	if message == "" {
		message = b.code.DefaultMessage()
	}

	var details map[string]interface{}
	if len(b.details) > 0 {
		details = b.details
	}

	return &UCXLErrorResponse{
		Error: UCXLError{
			Code:      b.code,
			Message:   message,
			Details:   details,
			Source:    b.source,
			Path:      b.path,
			RequestID: b.requestID,
			Timestamp: time.Now().UTC(),
			Cause:     b.cause,
		},
	}
}

// ResponseBuilder helps build standardized success responses
type ResponseBuilder struct {
	code      UCXLResponseCode
	message   string
	data      interface{}
	details   map[string]interface{}
	requestID string
}

// NewResponseBuilder creates a new response builder with the specified code
func NewResponseBuilder(code UCXLResponseCode) *ResponseBuilder {
	return &ResponseBuilder{
		code:      code,
		details:   make(map[string]interface{}),
		requestID: fmt.Sprintf("req-%s", uuid.New().String()[:8]),
	}
}

// Message sets a custom success message
func (b *ResponseBuilder) Message(message string) *ResponseBuilder {
	b.message = message
	return b
}

// Data sets the response data
func (b *ResponseBuilder) Data(data interface{}) *ResponseBuilder {
	b.data = data
	return b
}

// Detail adds a detail field
func (b *ResponseBuilder) Detail(key string, value interface{}) *ResponseBuilder {
	b.details[key] = value
	return b
}

// RequestID sets the request ID
func (b *ResponseBuilder) RequestID(requestID string) *ResponseBuilder {
	b.requestID = requestID
	return b
}

// Build creates the success response
func (b *ResponseBuilder) Build() *UCXLSuccessResponse {
	message := b.message
	if message == "" {
		message = b.code.DefaultMessage()
	}

	var details map[string]interface{}
	if len(b.details) > 0 {
		details = b.details
	}

	return &UCXLSuccessResponse{
		Response: UCXLResponse{
			Code:      b.code,
			Message:   message,
			Data:      b.data,
			Details:   details,
			RequestID: b.requestID,
			Timestamp: time.Now().UTC(),
		},
	}
}

// JSON marshaling helpers

// ToJSON converts the error response to JSON
func (e *UCXLErrorResponse) ToJSON() ([]byte, error) {
	return json.Marshal(e)
}

// FromJSON creates an error response from JSON
func (e *UCXLErrorResponse) FromJSON(data []byte) error {
	return json.Unmarshal(data, e)
}

// ToJSON converts the success response to JSON
func (r *UCXLSuccessResponse) ToJSON() ([]byte, error) {
	return json.Marshal(r)
}

// FromJSON creates a success response from JSON
func (r *UCXLSuccessResponse) FromJSON(data []byte) error {
	return json.Unmarshal(data, r)
}