package controllers

import (
	"math"
	"strconv"

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

// PaginationHelper provides pagination utilities
type PaginationHelper struct{}

// PaginationParams represents pagination parameters
type PaginationParams struct {
	Page     int `json:"page"`
	PageSize int `json:"page_size"`
	Offset   int `json:"-"`
}

// PaginationMeta represents pagination metadata
type PaginationMeta struct {
	Page       int   `json:"page"`
	PageSize   int   `json:"page_size"`
	Total      int64 `json:"total"`
	TotalPages int   `json:"total_pages"`
	HasNext    bool  `json:"has_next"`
	HasPrev    bool  `json:"has_prev"`
}

// NewPaginationHelper creates a new PaginationHelper instance
func NewPaginationHelper() *PaginationHelper {
	return &PaginationHelper{}
}

// GetPaginationParams extracts pagination parameters from the request
// Defaults: page=1, pageSize=20
func (ph *PaginationHelper) GetPaginationParams(c *gin.Context) PaginationParams {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))

	// Validate and constrain values
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 20
	}
	if pageSize > 100 {
		pageSize = 100 // Max page size to prevent abuse
	}

	offset := (page - 1) * pageSize

	return PaginationParams{
		Page:     page,
		PageSize: pageSize,
		Offset:   offset,
	}
}

// BuildPaginationMeta creates pagination metadata
func (ph *PaginationHelper) BuildPaginationMeta(params PaginationParams, total int64) PaginationMeta {
	totalPages := int(math.Ceil(float64(total) / float64(params.PageSize)))
	
	return PaginationMeta{
		Page:       params.Page,
		PageSize:   params.PageSize,
		Total:      total,
		TotalPages: totalPages,
		HasNext:    params.Page < totalPages,
		HasPrev:    params.Page > 1,
	}
}

// Paginate applies pagination to a GORM query and returns the paginated results with metadata
func (ph *PaginationHelper) Paginate(c *gin.Context, db *gorm.DB, dest interface{}) (PaginationMeta, error) {
	params := ph.GetPaginationParams(c)
	
	// Count total records
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return PaginationMeta{}, err
	}
	
	// Apply pagination
	if err := db.Offset(params.Offset).Limit(params.PageSize).Find(dest).Error; err != nil {
		return PaginationMeta{}, err
	}
	
	meta := ph.BuildPaginationMeta(params, total)
	return meta, nil
}

// PaginateWithPreload applies pagination with preloading associations
func (ph *PaginationHelper) PaginateWithPreload(c *gin.Context, db *gorm.DB, dest interface{}, preloads ...string) (PaginationMeta, error) {
	params := ph.GetPaginationParams(c)
	
	// Count total records (without preload)
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return PaginationMeta{}, err
	}
	
	// Apply preloads
	query := db
	for _, preload := range preloads {
		query = query.Preload(preload)
	}
	
	// Apply pagination
	if err := query.Offset(params.Offset).Limit(params.PageSize).Find(dest).Error; err != nil {
		return PaginationMeta{}, err
	}
	
	meta := ph.BuildPaginationMeta(params, total)
	return meta, nil
}

// Global PaginationHelper instance for convenience
var Paginator = NewPaginationHelper()
