package errors

import (
	"encoding/json"
	"fmt"
	"net/http"
	"service/pkg/logger"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// HTTPErrorResponse represents standardized HTTP error response
type HTTPErrorResponse struct {
	Error struct {
		Code      string                 `json:"code"`
		Message   string                 `json:"message"`
		Details   map[string]interface{} `json:"details,omitempty"`
		RequestID string                 `json:"request_id,omitempty"`
		Timestamp string                 `json:"timestamp"`
		Retryable bool                   `json:"retryable,omitempty"`
	} `json:"error"`
	Meta struct {
		HTTPStatus int    `json:"http_status"`
		Category   string `json:"category"`
	} `json:"meta,omitempty"`
}

// HTTPMiddleware creates error handling middleware for Gin
func HTTPMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()

		// Check if there are any errors
		if len(c.Errors) > 0 {
			// Get the last error
			err := c.Errors.Last().Err
			HandleHTTPError(c, err)
		}
	}
}

// HandleHTTPError handles HTTP error response
func HandleHTTPError(c *gin.Context, err error) {
	appErr := FromError(err)
	if appErr == nil {
		appErr = NewWithCode(ErrCodeInternalError, "Unknown error")
	}

	// Create error response
	response := &HTTPErrorResponse{}
	response.Error.Code = appErr.Code()
	response.Error.Message = appErr.GetLocalizedMessage(getLanguageFromContext(c))
	response.Error.Details = appErr.Metadata()
	response.Error.RequestID = c.GetString("request_id")
	response.Error.Timestamp = appErr.Metadata()["timestamp"].(string)
	response.Error.Retryable = IsRetryable(appErr.Code())
	response.Meta.HTTPStatus = appErr.HTTPStatus()
	response.Meta.Category = appErr.Category()

	// Log error
	LogHTTPError(c, appErr)

	// Send response
	c.JSON(appErr.HTTPStatus(), response)
}

// getLanguageFromContext extracts language from context
func getLanguageFromContext(c *gin.Context) string {
	// Try Accept-Language header
	lang := c.GetHeader("Accept-Language")
	if lang != "" {
		return parseAcceptLanguage(lang)
	}

	// Try query parameter
	lang = c.Query("lang")
	if lang != "" {
		return lang
	}

	// Default to English
	return "en"
}

// parseAcceptLanguage parses Accept-Language header
func parseAcceptLanguage(header string) string {
	// Simple parsing - take first language
	for idx := 0; idx < len(header); idx++ {
		if header[idx] == ',' || header[idx] == ';' {
			return header[:idx]
		}
	}
	return header
}

// LogHTTPError logs HTTP error with context
func LogHTTPError(c *gin.Context, err Error) {
	log := logger.Default()

	log.Error("HTTP error occurred",
		logger.String("error_code", err.Code()),
		logger.String("category", err.Category()),
		logger.Int("http_status", err.HTTPStatus()),
		logger.Any("metadata", err.Metadata()),
		logger.String("path", c.Request.URL.Path),
		logger.String("method", c.Request.Method),
	)
}

// ValidationErrorHandler handles validation errors specifically
func ValidationErrorHandler(c *gin.Context, err error) {
	appErr := FromError(err)
	if appErr == nil {
		appErr = NewWithCode(ErrCodeValidationFailed, err.Error())
	}

	// Enhance validation errors with field details
	if appErr.Category() == CategoryValidation {
		if details, ok := appErr.Metadata()["validation_errors"]; ok {
			response := gin.H{
				"error": gin.H{
					"code":    appErr.Code(),
					"message": appErr.GetLocalizedMessage(getLanguageFromContext(c)),
					"fields":  details,
				},
			}
			c.JSON(http.StatusBadRequest, response)
			return
		}
	}

	HandleHTTPError(c, appErr)
}

// PanicHandler handles panics
func PanicHandler(c *gin.Context, recovered interface{}) {
	var err error

	switch x := recovered.(type) {
	case string:
		err = NewWithCode(ErrCodeInternalError, x)
	case error:
		err = FromError(x)
	default:
		err = NewWithCode(ErrCodeUnexpectedError, fmt.Sprintf("Unknown panic: %v", x))
	}

	HandleHTTPError(c, err)
}

// WriteErrorResponse writes error response directly
func WriteErrorResponse(w http.ResponseWriter, err error) {
	appErr := FromError(err)
	if appErr == nil {
		appErr = NewWithCode(ErrCodeInternalError, "Unknown error")
	}

	response := &HTTPErrorResponse{}
	response.Error.Code = appErr.Code()
	response.Error.Message = appErr.GetLocalizedMessage("en")
	response.Error.Details = appErr.Metadata()
	response.Error.Timestamp = appErr.Metadata()["timestamp"].(string)
	response.Error.Retryable = IsRetryable(appErr.Code())
	response.Meta.HTTPStatus = appErr.HTTPStatus()
	response.Meta.Category = appErr.Category()

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(appErr.HTTPStatus())
	json.NewEncoder(w).Encode(response)
}

// sanitizeError removes sensitive information from errors
func sanitizeError(err error) error {
	appErr := FromError(err)
	if appErr == nil {
		return err
	}

	// Remove sensitive metadata
	sanitized := make(Metadata)
	for k, v := range appErr.Metadata() {
		if !isSensitiveField(k) {
			sanitized[k] = v
		}
	}

	return NewWithMetadata(appErr.Code(), appErr.Error(), sanitized)
}

// isSensitiveField checks if field contains sensitive information
func isSensitiveField(field string) bool {
	sensitiveFields := []string{"password", "token", "secret", "key", "auth"}
	for _, sensitive := range sensitiveFields {
		if strings.Contains(strings.ToLower(field), sensitive) {
			return true
		}
	}
	return false
}

// generateRequestID generates a unique request ID
func generateRequestID() string {
	return fmt.Sprintf("req_%d", time.Now().UnixNano())
}
