package utils

import (
	"os"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
)

// ErrorCode represents standardized error codes
type ErrorCode string

const (
	// General error codes
	ErrCodeValidation   ErrorCode = "VALIDATION_ERROR"
	ErrCodeNotFound     ErrorCode = "NOT_FOUND"
	ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED"
	ErrCodeForbidden    ErrorCode = "FORBIDDEN"
	ErrCodeConflict     ErrorCode = "CONFLICT"
	ErrCodeInternal     ErrorCode = "INTERNAL_ERROR"
	ErrCodeBadRequest   ErrorCode = "BAD_REQUEST"
	ErrCodeTimeout      ErrorCode = "TIMEOUT"
	ErrCodeRateLimit    ErrorCode = "RATE_LIMIT"

	// Business logic error codes
	ErrCodeInvalidCredentials ErrorCode = "INVALID_CREDENTIALS"
	ErrCodeTokenExpired       ErrorCode = "TOKEN_EXPIRED"
	ErrCodeTokenInvalid       ErrorCode = "TOKEN_INVALID"
	ErrCodeInsufficientFunds  ErrorCode = "INSUFFICIENT_FUNDS"
	ErrCodeDuplicateEntry     ErrorCode = "DUPLICATE_ENTRY"
	ErrCodeOperationFailed    ErrorCode = "OPERATION_FAILED"
)

// Response represents the standard API response structure
type Response struct {
	Success   bool        `json:"success"`
	Message   string      `json:"message"`
	Data      interface{} `json:"data,omitempty"`
	Code      ErrorCode   `json:"code,omitempty"`
	Details   interface{} `json:"details,omitempty"`
	Timestamp time.Time   `json:"timestamp"`
	RequestID string      `json:"request_id,omitempty"`
}

// PaginatedResponse represents paginated API response
type PaginatedResponse struct {
	Success   bool        `json:"success"`
	Message   string      `json:"message"`
	Data      interface{} `json:"data"`
	Meta      interface{} `json:"meta"`
	Timestamp time.Time   `json:"timestamp"`
	RequestID string      `json:"request_id,omitempty"`
}

// ErrorDetail provides additional error information
type ErrorDetail struct {
	Field string      `json:"field,omitempty"`
	Value interface{} `json:"value,omitempty"`
	Issue string      `json:"issue"`
	Code  string      `json:"code,omitempty"`
}

// SuccessResponse returns a successful API response
func SuccessResponse(c *fiber.Ctx, message string, data interface{}) error {
	response := Response{
		Success:   true,
		Message:   message,
		Data:      data,
		Timestamp: time.Now(),
		RequestID: getRequestID(c),
	}

	return c.Status(fiber.StatusOK).JSON(response)
}

// PaginatedSuccessResponse returns a successful paginated API response
func PaginatedSuccessResponse(c *fiber.Ctx, message string, data interface{}, meta interface{}) error {
	response := PaginatedResponse{
		Success:   true,
		Message:   message,
		Data:      data,
		Meta:      meta,
		Timestamp: time.Now(),
		RequestID: getRequestID(c),
	}

	return c.Status(fiber.StatusOK).JSON(response)
}

// ErrorResponse returns an error API response
func ErrorResponse(c *fiber.Ctx, status int, message string) error {
	code := getErrorCodeFromStatus(status)

	response := Response{
		Success:   false,
		Message:   message,
		Code:      code,
		Timestamp: time.Now(),
		RequestID: getRequestID(c),
	}

	return c.Status(status).JSON(response)
}

// DetailedErrorResponse returns an error response with additional details
func DetailedErrorResponse(c *fiber.Ctx, status int, message string, code ErrorCode, details interface{}) error {
	response := Response{
		Success:   false,
		Message:   message,
		Code:      code,
		Details:   details,
		Timestamp: time.Now(),
		RequestID: getRequestID(c),
	}

	return c.Status(status).JSON(response)
}

// ValidationErrorResponse returns a validation error response
func ValidationErrorResponse(c *fiber.Ctx, message string, validationErrors interface{}) error {
	return DetailedErrorResponse(
		c,
		fiber.StatusBadRequest,
		message,
		ErrCodeValidation,
		validationErrors,
	)
}

// NotFoundResponse returns a not found error response
func NotFoundResponse(c *fiber.Ctx, resource string) error {
	message := resource + " not found"
	return DetailedErrorResponse(
		c,
		fiber.StatusNotFound,
		message,
		ErrCodeNotFound,
		nil,
	)
}

// UnauthorizedResponse returns an unauthorized error response
func UnauthorizedResponse(c *fiber.Ctx, message string) error {
	if message == "" {
		message = "Authentication required"
	}

	return DetailedErrorResponse(
		c,
		fiber.StatusUnauthorized,
		message,
		ErrCodeUnauthorized,
		nil,
	)
}

// ForbiddenResponse returns a forbidden error response
func ForbiddenResponse(c *fiber.Ctx, message string) error {
	if message == "" {
		message = "Access denied"
	}

	return DetailedErrorResponse(
		c,
		fiber.StatusForbidden,
		message,
		ErrCodeForbidden,
		nil,
	)
}

// ConflictResponse returns a conflict error response
func ConflictResponse(c *fiber.Ctx, message string) error {
	return DetailedErrorResponse(
		c,
		fiber.StatusConflict,
		message,
		ErrCodeConflict,
		nil,
	)
}

// InternalErrorResponse returns an internal server error response
func InternalErrorResponse(c *fiber.Ctx, message string) error {
	safeMessage := "Internal server error"
	if shouldExposeInternalError() {
		trimmed := strings.TrimSpace(message)
		if trimmed != "" {
			safeMessage = trimmed
		}
	}

	return DetailedErrorResponse(
		c,
		fiber.StatusInternalServerError,
		safeMessage,
		ErrCodeInternal,
		nil,
	)
}

func shouldExposeInternalError() bool {
	env := strings.ToLower(strings.TrimSpace(os.Getenv("ENV")))
	return env == "development"
}

// getRequestID extracts request ID from context
func getRequestID(c *fiber.Ctx) string {
	if requestID := c.Locals("requestId"); requestID != nil {
		if id, ok := requestID.(string); ok {
			return id
		}
	}
	return ""
}

// getErrorCodeFromStatus maps HTTP status to error code
func getErrorCodeFromStatus(status int) ErrorCode {
	switch status {
	case fiber.StatusBadRequest:
		return ErrCodeBadRequest
	case fiber.StatusUnauthorized:
		return ErrCodeUnauthorized
	case fiber.StatusForbidden:
		return ErrCodeForbidden
	case fiber.StatusNotFound:
		return ErrCodeNotFound
	case fiber.StatusConflict:
		return ErrCodeConflict
	case fiber.StatusTooManyRequests:
		return ErrCodeRateLimit
	case fiber.StatusRequestTimeout:
		return ErrCodeTimeout
	case fiber.StatusInternalServerError:
		return ErrCodeInternal
	default:
		return ErrCodeInternal
	}
}
