// Package client provides a Go SDK for interacting with the MapleFile API.
package client

import (
	"encoding/json"
	"fmt"
	"strings"
)

// ProblemDetail represents an RFC 9457 problem detail response from the API.
type ProblemDetail struct {
	Type      string            `json:"type"`
	Status    int               `json:"status"`
	Title     string            `json:"title"`
	Detail    string            `json:"detail,omitempty"`
	Instance  string            `json:"instance,omitempty"`
	Errors    map[string]string `json:"errors,omitempty"`
	Timestamp string            `json:"timestamp"`
	TraceID   string            `json:"trace_id,omitempty"`
}

// APIError wraps ProblemDetail for the error interface.
type APIError struct {
	ProblemDetail
}

// Error returns a formatted error message from the ProblemDetail.
func (e *APIError) Error() string {
	var errMsg strings.Builder

	if e.Detail != "" {
		errMsg.WriteString(e.Detail)
	} else {
		errMsg.WriteString(e.Title)
	}

	if len(e.Errors) > 0 {
		errMsg.WriteString("\n\nValidation errors:")
		for field, message := range e.Errors {
			errMsg.WriteString(fmt.Sprintf("\n  - %s: %s", field, message))
		}
	}

	return errMsg.String()
}

// StatusCode returns the HTTP status code from the error.
func (e *APIError) StatusCode() int {
	return e.Status
}

// GetValidationErrors returns the validation errors map.
func (e *APIError) GetValidationErrors() map[string]string {
	return e.Errors
}

// GetFieldError returns the error message for a specific field, or empty string if not found.
func (e *APIError) GetFieldError(field string) string {
	if e.Errors == nil {
		return ""
	}
	return e.Errors[field]
}

// HasFieldError checks if a specific field has a validation error.
func (e *APIError) HasFieldError(field string) bool {
	if e.Errors == nil {
		return false
	}
	_, exists := e.Errors[field]
	return exists
}

// IsNotFound checks if the error is a 404 Not Found error.
func IsNotFound(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return apiErr.Status == 404
	}
	return false
}

// IsUnauthorized checks if the error is a 401 Unauthorized error.
func IsUnauthorized(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return apiErr.Status == 401
	}
	return false
}

// IsForbidden checks if the error is a 403 Forbidden error.
func IsForbidden(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return apiErr.Status == 403
	}
	return false
}

// IsValidationError checks if the error has validation errors.
func IsValidationError(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return len(apiErr.Errors) > 0
	}
	return false
}

// IsConflict checks if the error is a 409 Conflict error.
func IsConflict(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return apiErr.Status == 409
	}
	return false
}

// IsTooManyRequests checks if the error is a 429 Too Many Requests error.
func IsTooManyRequests(err error) bool {
	if apiErr, ok := err.(*APIError); ok {
		return apiErr.Status == 429
	}
	return false
}

// parseErrorResponse attempts to parse an error response body into an APIError.
// It tries RFC 9457 format first, then falls back to legacy format.
//
// Note: RFC 9457 specifies that error responses should use Content-Type: application/problem+json,
// but we parse based on the response structure rather than Content-Type for maximum compatibility.
func parseErrorResponse(body []byte, statusCode int) error {
	// Try to parse as RFC 9457 ProblemDetail
	// The presence of the "type" field distinguishes RFC 9457 from legacy responses
	var problem ProblemDetail
	if err := json.Unmarshal(body, &problem); err == nil && problem.Type != "" {
		return &APIError{ProblemDetail: problem}
	}

	// Fallback for non-RFC 9457 errors
	var errorResponse map[string]interface{}
	if err := json.Unmarshal(body, &errorResponse); err == nil {
		if errMsg, ok := errorResponse["message"].(string); ok {
			return &APIError{
				ProblemDetail: ProblemDetail{
					Status: statusCode,
					Title:  errMsg,
					Detail: errMsg,
				},
			}
		}
	}

	// Last resort: return raw body as error
	return &APIError{
		ProblemDetail: ProblemDetail{
			Status: statusCode,
			Title:  fmt.Sprintf("HTTP %d", statusCode),
			Detail: string(body),
		},
	}
}
