package utils

import (
	"math"
	"reflect"

	"github.com/tidbcloud/account/internel/dto"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

const (
	DEFAULTPAGESIZE = 100
	DEFAULTPAGE     = 1
)

type Paginator struct {
	Page       uint32      `json:"page"`
	PageSize   uint32      `json:"page_size"`
	Total      uint64      `json:"total"`
	TotalPages uint64      `json:"total_pages"`
	Data       interface{} `json:"data"`
}

func NewPaginator(page, pageSize uint32, total uint64, data interface{}) *Paginator {
	totalPages := uint64(math.Ceil(float64(total) / float64(pageSize)))
	return &Paginator{
		Page:       page,
		PageSize:   pageSize,
		Total:      total,
		TotalPages: totalPages,
		Data:       data,
	}
}

func Paginate(db *gorm.DB, params dto.PageParams, model interface{}) (*Paginator, error) {
	var total int64
	var offset int
	page := params.Page
	if page <= 0 {
		page = DEFAULTPAGE
	}

	pageSize := params.PageSize
	if pageSize == 0 {
		pageSize = DEFAULTPAGESIZE
	}
	offset = int((page - 1) * pageSize) // nolint

	// simple filter conditions
	for key, value := range params.Filters {
		db = db.Where(key+" = ?", value)
	}

	// advanced filter conditions
	for key, value := range params.AdvancedFilters {
		db = db.Where(key, value)
	}

	if params.SortBy != "" && params.Order != "" {
		db = db.Order(params.SortBy + " " + params.Order)
	}

	// count the total number of records
	if err := db.Model(model).Count(&total).Error; err != nil {
		return nil, err
	}

	// Get the type of the model
	elementType := reflect.TypeOf(model).Elem()

	sliceType := reflect.SliceOf(elementType)

	// Use reflect.MakeSlice to create an empty slice of type
	data := reflect.MakeSlice(sliceType, 0, 0).Interface()

	// Execute the query with pagination
	if err := db.Offset(offset).Limit(int(pageSize)).Find(&data).Error; err != nil {
		tilog.PlainInfo("error", zap.Error(err))
		return nil, err
	}
	paginator := NewPaginator(page, pageSize, uint64(total), data)
	return paginator, nil
}
