package wmo

import (
	"errors"
	"go.mongodb.org/mongo-driver/bson"
	ct "gogs.mikescher.com/BlackForestBytes/goext/cursortoken"
)

func CreatePagination[TData any](coll *Coll[TData], token ct.CursorToken, fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection, pageSize *int) ([]bson.D, error) {

	cond := bson.A{}
	sort := bson.D{}

	valuePrimary, err := coll.getTokenValueAsMongoType(token.ValuePrimary, fieldPrimary)
	if err != nil {
		return nil, err
	}

	if sortPrimary == ct.SortASC {
		// We sort ASC on <field> - so we want all entries newer ($gt) than the $primary
		cond = append(cond, bson.M{fieldPrimary: bson.M{"$gt": valuePrimary}})
		sort = append(sort, bson.E{Key: fieldPrimary, Value: +1})
	} else if sortPrimary == ct.SortDESC {
		// We sort DESC on <field> - so we want all entries older ($lt) than the $primary
		cond = append(cond, bson.M{fieldPrimary: bson.M{"$lt": valuePrimary}})
		sort = append(sort, bson.E{Key: fieldPrimary, Value: -1})
	}

	if fieldSecondary != nil && sortSecondary != nil && *fieldSecondary != fieldPrimary {

		valueSecondary, err := coll.getTokenValueAsMongoType(token.ValueSecondary, *fieldSecondary)
		if err != nil {
			return nil, err
		}

		if *sortSecondary == ct.SortASC {

			// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
			cond = append(cond, bson.M{"$and": bson.A{
				bson.M{fieldPrimary: valuePrimary},
				bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
			}})

			sort = append(sort, bson.E{Key: fieldPrimary, Value: +1})

		} else if *sortSecondary == ct.SortDESC {

			// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
			cond = append(cond, bson.M{"$and": bson.A{
				bson.M{fieldPrimary: valuePrimary},
				bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
			}})

			sort = append(sort, bson.E{Key: fieldPrimary, Value: -1})

		}
	}

	pipeline := make([]bson.D, 0, 3)

	if token.Mode == ct.CTMStart {

		// no gt/lt condition

	} else if token.Mode == ct.CTMNormal {

		pipeline = append(pipeline, bson.D{{Key: "$match", Value: bson.M{"$or": cond}}})

	} else if token.Mode == ct.CTMEnd {

		// false
		pipeline = append(pipeline, bson.D{{Key: "$match", Value: bson.M{"$expr": bson.M{"$eq": bson.A{"1", "0"}}}}})

	} else {

		return nil, errors.New("unknown ct mode: " + string(token.Mode))

	}

	pipeline = append(pipeline, bson.D{{Key: "$sort", Value: sort}})

	if pageSize != nil {
		pipeline = append(pipeline, bson.D{{Key: "$limit", Value: int64(*pageSize + 1)}})
	}

	return pipeline, nil
}
