package trim

import (
	"bytes"
	"encoding/json"
	"strings"

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

// JSONBody trims whitespace from string fields in JSON request bodies.
func JSONBody() fiber.Handler {
	return func(c *fiber.Ctx) error {
		contentType := c.Get(fiber.HeaderContentType)
		if !strings.Contains(contentType, fiber.MIMEApplicationJSON) {
			return c.Next()
		}

		if strings.EqualFold(c.Path(), "/api/sso/users/sync") {
			return c.Next()
		}

		body := c.Body()
		if len(body) == 0 {
			return c.Next()
		}

		var payload any
		if err := json.Unmarshal(body, &payload); err != nil {
			return c.Next()
		}

		trimStrings(payload)

		buf := bytes.Buffer{}
		encoder := json.NewEncoder(&buf)
		encoder.SetEscapeHTML(false)
		if err := encoder.Encode(payload); err != nil {
			return err
		}

		trimmedBody := bytes.TrimSpace(buf.Bytes())
		c.Request().SetBody(trimmedBody)

		return c.Next()
	}
}

func trimStrings(value any) {
	switch v := value.(type) {
	case map[string]any:
		for key, val := range v {
			if str, ok := val.(string); ok {
				v[key] = strings.TrimSpace(str)
				continue
			}
			trimStrings(val)
		}
	case []any:
		for i, elem := range v {
			if str, ok := elem.(string); ok {
				v[i] = strings.TrimSpace(str)
				continue
			}
			trimStrings(elem)
		}
	}
}
