package sq

import (
	"context"
	"fmt"
	"gogs.mikescher.com/BlackForestBytes/goext/exerr"
)

func Iterate[TData any](ctx context.Context, q Queryable, table string, filter PaginateFilter, scanMode StructScanMode, scanSec StructScanSafety, page int, limit *int, consumer func(ctx context.Context, v TData) error) (int, error) {
	if filter == nil {
		filter = NewEmptyPaginateFilter()
	}

	prepParams := PP{}

	sortOrder := filter.Sort()
	sortCond := ""
	if len(sortOrder) > 0 {
		sortCond = "ORDER BY "
		for i, v := range sortOrder {
			if i > 0 {
				sortCond += ", "
			}
			sortCond += v.Field + " " + string(v.Direction)
		}
	}

	pageCond := ""
	if limit != nil {
		pageCond += fmt.Sprintf("LIMIT :%s OFFSET :%s", prepParams.Add(*limit+1), prepParams.Add(*limit*(page-1)))
	}

	filterCond, joinCond, joinTables := filter.SQL(prepParams)

	selectCond := table + ".*"
	for _, v := range joinTables {
		selectCond += ", " + v + ".*"
	}

	sqlQueryData := "SELECT " + selectCond + " FROM " + table + " " + joinCond + " WHERE ( " + filterCond + " ) " + sortCond + " " + pageCond

	rows, err := q.Query(ctx, sqlQueryData, prepParams)
	if err != nil {
		return 0, exerr.Wrap(err, "failed to list paginated entries from DB").Str("table", table).Any("filter", filter).Int("page", page).Any("limit", limit).Build()
	}

	return IterateAll[TData](ctx, q, rows, scanMode, scanSec, true, consumer)
}