package wmo

import (
	"context"
	"go.mongodb.org/mongo-driver/bson"
	"gogs.mikescher.com/BlackForestBytes/goext/exerr"
)

func (c *Coll[TData]) decodeSingle(ctx context.Context, dec Decodable) (TData, error) {

	var res TData
	var err error

	if c.customDecoder != nil {
		res, err = (*c.customDecoder)(ctx, dec)
		if err != nil {
			return *new(TData), exerr.Wrap(err, "failed to decode single entity with custom-decoder").Type("decoder", *c.customDecoder).NoLog().Build()
		}
	} else {
		err = dec.Decode(&res)
		if err != nil {
			return *new(TData), exerr.Wrap(err, "failed to decode single entity").Type("target-type", res).NoLog().Build()
		}
	}

	for _, hook := range c.unmarshalHooks {
		res = hook(res)
	}

	return res, nil
}

func (c *Coll[TData]) decodeAll(ctx context.Context, cursor Cursorable) ([]TData, error) {

	res := make([]TData, 0, cursor.RemainingBatchLength())

	if c.customDecoder != nil {
		for cursor.Next(ctx) {
			entry, err := (*c.customDecoder)(ctx, cursor)
			if err != nil {
				return nil, exerr.Wrap(err, "failed to decode entity with custom-decoder").Type("decoder", *c.customDecoder).Build()
			}
			res = append(res, entry)
		}
	} else {
		err := cursor.All(ctx, &res)
		if err != nil {
			return nil, exerr.Wrap(err, "failed to batch-decode entity").Type("target-type", res).Build()
		}
	}

	for i := 0; i < len(res); i++ {
		for _, hook := range c.unmarshalHooks {
			res[i] = hook(res[i])
		}
	}

	return res, nil

}

func (c *Coll[TData]) decodeSingleOrRequery(ctx context.Context, dec Decodable) (TData, error) {
	if c.extraModPipeline == nil {

		// simple case, we can just decode the result and return it
		return c.decodeSingle(ctx, dec)

	} else {

		// annyoing case, we have a extraModPipeline and need to re-query the document such that the extraModPipeline is applied...

		type genDoc struct {
			ID any `bson:"_id"`
		}
		var res genDoc
		err := dec.Decode(&res)
		if err != nil {
			return *new(TData), exerr.Wrap(err, "failed to ID-decode entity").NoLog().Build()
		}

		v, err := c.findOneInternal(ctx, bson.M{"_id": res.ID}, false)
		if err != nil {
			return *new(TData), exerr.Wrap(err, "failed to re-query entity").Any("_id", res.ID).NoLog().Build()
		}

		return *v, nil

	}
}