package response

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/labstack/echo/v4"
)

const (
	// DefaultListLimit is the default page size for admin list endpoints.
	DefaultListLimit = 10
	// MaxListLimit is the maximum allowed page size.
	MaxListLimit = 100
)

var (
	// ErrInvalidPagination is returned when pagination query parameters are invalid.
	ErrInvalidPagination = errors.New("invalid pagination parameters")
	// ErrPaginationConflict is returned when cursor and offset are both supplied.
	ErrPaginationConflict = errors.New("cursor and offset pagination cannot be used together")
)

// NewPagination parses and validates hybrid pagination query parameters from the request.
func NewPagination(c echo.Context) (*Pagination, error) {
	limit := DefaultListLimit
	if limitStr := c.QueryParam("limit"); limitStr != "" {
		parsed, err := strconv.Atoi(limitStr)
		if err != nil {
			return nil, fmt.Errorf("%w: limit must be an integer", ErrInvalidPagination)
		}
		limit = parsed
	}
	if limit < 1 || limit > MaxListLimit {
		return nil, fmt.Errorf("%w: limit must be between 1 and %d", ErrInvalidPagination, MaxListLimit)
	}

	cursor := c.QueryParam("cursor")
	offsetParam := c.QueryParam("offset")
	if cursor != "" && offsetParam != "" {
		return nil, ErrPaginationConflict
	}

	offset := 0
	if offsetParam != "" {
		parsed, err := strconv.Atoi(offsetParam)
		if err != nil {
			return nil, fmt.Errorf("%w: offset must be an integer", ErrInvalidPagination)
		}
		if parsed < 0 {
			return nil, fmt.Errorf("%w: offset cannot be negative", ErrInvalidPagination)
		}
		offset = parsed
	}

	sortBy := c.QueryParam("sort_by")
	if sortBy == "" {
		sortBy = "created_at"
	}

	sortOrder := c.QueryParam("sort_order")
	if sortOrder == "" {
		sortOrder = "desc"
	}

	return &Pagination{
		Limit:     limit,
		Offset:    offset,
		Cursor:    cursor,
		SortBy:    sortBy,
		SortOrder: sortOrder,
	}, nil
}

// ListMeta builds pagination metadata for a list page.
// pageOffset is the zero-based index of the first item in this page (from offset or cursor).
func (p *Pagination) ListMeta(total int64, nextCursor string, hasMore bool, pageOffset int) *Meta {
	meta := &Meta{
		Limit:   p.Limit,
		Offset:  pageOffset,
		HasMore: hasMore,
	}
	if hasMore && nextCursor != "" {
		meta.NextCursor = nextCursor
	}
	if total >= 0 {
		meta.Total = int(total)
		if p.Limit > 0 {
			meta.Page = pageOffset/p.Limit + 1
			meta.Pages = int((total + int64(p.Limit) - 1) / int64(p.Limit))
		}
	}
	return meta
}

// ListMetaFromPage builds metadata from a paginated repository result.
func (p *Pagination) ListMetaFromPage(total int64, nextCursor string, hasMore bool, pageOffset int) *Meta {
	return p.ListMeta(total, nextCursor, hasMore, pageOffset)
}

// IsPaginationError reports whether err is a client-facing pagination error.
func IsPaginationError(err error) bool {
	return errors.Is(err, ErrInvalidPagination) || errors.Is(err, ErrPaginationConflict)
}
