//! Utilitaires de pagination optimisée
//!
//! Ce module implémente la pagination cursor-based qui est plus performante
//! que la pagination offset-based pour les grandes datasets.

package utils

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// PaginationRequest représente une requête de pagination
type PaginationRequest struct {
	Limit  int    `json:"limit" form:"limit"`
	Cursor string `json:"cursor" form:"cursor"`
}

// PaginationResponse représente une réponse paginée
type PaginationResponse struct {
	Data       interface{} `json:"data"`
	NextCursor string      `json:"next_cursor,omitempty"`
	PrevCursor string      `json:"prev_cursor,omitempty"`
	HasNext    bool        `json:"has_next"`
	HasPrev    bool        `json:"has_prev"`
	Total      int64       `json:"total,omitempty"`
}

// Cursor représente un curseur de pagination
type Cursor struct {
	ID        string    `json:"id"`
	CreatedAt time.Time `json:"created_at"`
}

// EncodeCursor encode un curseur en string base64
func EncodeCursor(cursor *Cursor) (string, error) {
	if cursor == nil {
		return "", nil
	}

	data, err := json.Marshal(cursor)
	if err != nil {
		return "", fmt.Errorf("failed to marshal cursor: %w", err)
	}

	return base64.URLEncoding.EncodeToString(data), nil
}

// DecodeCursor décode un curseur depuis une string base64
func DecodeCursor(cursorStr string) (*Cursor, error) {
	if cursorStr == "" {
		return nil, nil
	}

	data, err := base64.URLEncoding.DecodeString(cursorStr)
	if err != nil {
		return nil, fmt.Errorf("failed to decode cursor: %w", err)
	}

	var cursor Cursor
	if err := json.Unmarshal(data, &cursor); err != nil {
		return nil, fmt.Errorf("failed to unmarshal cursor: %w", err)
	}

	return &cursor, nil
}

// CreateCursor crée un nouveau curseur à partir d'un ID et d'une date
func CreateCursor(id string, createdAt time.Time) *Cursor {
	return &Cursor{
		ID:        id,
		CreatedAt: createdAt,
	}
}

// ValidatePaginationRequest valide une requête de pagination
func ValidatePaginationRequest(req *PaginationRequest) error {
	if req.Limit <= 0 {
		req.Limit = 20 // Valeur par défaut
	}

	if req.Limit > 100 {
		req.Limit = 100 // Limite maximale
	}

	return nil
}

// BuildPaginationResponse construit une réponse paginée
func BuildPaginationResponse(
	data interface{},
	nextCursor *Cursor,
	prevCursor *Cursor,
	hasNext bool,
	hasPrev bool,
	total int64,
) (*PaginationResponse, error) {
	response := &PaginationResponse{
		Data:    data,
		HasNext: hasNext,
		HasPrev: hasPrev,
		Total:   total,
	}

	// Encoder le curseur suivant
	if nextCursor != nil {
		nextCursorStr, err := EncodeCursor(nextCursor)
		if err != nil {
			return nil, fmt.Errorf("failed to encode next cursor: %w", err)
		}
		response.NextCursor = nextCursorStr
	}

	// Encoder le curseur précédent
	if prevCursor != nil {
		prevCursorStr, err := EncodeCursor(prevCursor)
		if err != nil {
			return nil, fmt.Errorf("failed to encode prev cursor: %w", err)
		}
		response.PrevCursor = prevCursorStr
	}

	return response, nil
}

// ParseLimit parse et valide la limite de pagination
func ParseLimit(limitStr string, defaultLimit int) int {
	if limitStr == "" {
		return defaultLimit
	}

	limit, err := strconv.Atoi(limitStr)
	if err != nil || limit <= 0 {
		return defaultLimit
	}

	if limit > 100 {
		return 100
	}

	return limit
}

// ParseCursor parse et valide un curseur
func ParseCursor(cursorStr string) (*Cursor, error) {
	if cursorStr == "" {
		return nil, nil
	}

	return DecodeCursor(cursorStr)
}

// OffsetPaginationRequest représente une requête de pagination offset-based (legacy)
type OffsetPaginationRequest struct {
	Page  int `json:"page" form:"page"`
	Limit int `json:"limit" form:"limit"`
}

// OffsetPaginationResponse représente une réponse paginée offset-based
type OffsetPaginationResponse struct {
	Data       interface{} `json:"data"`
	Page       int         `json:"page"`
	Limit      int         `json:"limit"`
	Total      int64       `json:"total"`
	TotalPages int         `json:"total_pages"`
	HasNext    bool        `json:"has_next"`
	HasPrev    bool        `json:"has_prev"`
}

// BuildOffsetPaginationResponse construit une réponse paginée offset-based
func BuildOffsetPaginationResponse(
	data interface{},
	page int,
	limit int,
	total int64,
) *OffsetPaginationResponse {
	totalPages := int((total + int64(limit) - 1) / int64(limit))

	return &OffsetPaginationResponse{
		Data:       data,
		Page:       page,
		Limit:      limit,
		Total:      total,
		TotalPages: totalPages,
		HasNext:    page < totalPages,
		HasPrev:    page > 1,
	}
}

// ValidateOffsetPaginationRequest valide une requête de pagination offset-based
func ValidateOffsetPaginationRequest(req *OffsetPaginationRequest) error {
	if req.Page <= 0 {
		req.Page = 1
	}

	if req.Limit <= 0 {
		req.Limit = 20
	}

	if req.Limit > 100 {
		req.Limit = 100
	}

	return nil
}

// CalculateOffset calcule l'offset pour la pagination offset-based
func CalculateOffset(page, limit int) int {
	return (page - 1) * limit
}

// PaginationHelper contient des méthodes utilitaires pour la pagination
type PaginationHelper struct{}

// NewPaginationHelper crée un nouveau helper de pagination
func NewPaginationHelper() *PaginationHelper {
	return &PaginationHelper{}
}

// GetDefaultLimit retourne la limite par défaut
func (h *PaginationHelper) GetDefaultLimit() int {
	return 20
}

// GetMaxLimit retourne la limite maximale
func (h *PaginationHelper) GetMaxLimit() int {
	return 100
}

// ValidateLimit valide et ajuste une limite
func (h *PaginationHelper) ValidateLimit(limit int) int {
	if limit <= 0 {
		return h.GetDefaultLimit()
	}

	if limit > h.GetMaxLimit() {
		return h.GetMaxLimit()
	}

	return limit
}

// CreateEmptyResponse crée une réponse paginée vide
func (h *PaginationHelper) CreateEmptyResponse() *PaginationResponse {
	return &PaginationResponse{
		Data:    []interface{}{},
		HasNext: false,
		HasPrev: false,
		Total:   0,
	}
}
