package builderimp

import (
	"github.com/tech/sendico/pkg/db/repository/builder"
	"go.mongodb.org/mongo-driver/v2/bson"
)

type aliasImp struct {
	lhs builder.Field
	rhs any
}

func (a *aliasImp) Field() builder.Field {
	return a.lhs
}

func (a *aliasImp) Build() bson.D {
	return bson.D{{Key: a.lhs.Build(), Value: a.rhs}}
}

// 1. Null alias (_id: null)
func NewNullAlias(lhs builder.Field) builder.Alias {
	return &aliasImp{lhs: lhs, rhs: nil}
}

func NewAlias(lhs builder.Field, rhs any) builder.Alias {
	return &aliasImp{lhs: lhs, rhs: rhs}
}

// 2. Simple alias (_id: "$taskRef")
func NewSimpleAlias(lhs, rhs builder.Field) builder.Alias {
	return &aliasImp{lhs: lhs, rhs: rhs.Build()}
}

// 3. Complex alias (_id: { aliasName: "$originalField", ... })
type ComplexAlias struct {
	lhs builder.Field
	rhs []builder.Alias // Correcting handling of slice of aliases
}

func (a *ComplexAlias) Field() builder.Field {
	return a.lhs
}

func (a *ComplexAlias) Build() bson.D {
	fieldMap := bson.M{}

	for _, alias := range a.rhs {
		// Each alias.Build() still returns a bson.D
		aliasDoc := alias.Build()

		// 1. Marshal the ordered D into raw BSON bytes
		raw, err := bson.Marshal(aliasDoc)
		if err != nil {
			panic("Failed to marshal alias document: " + err.Error())
		}

		// 2. Unmarshal those bytes into an unordered M
		var docM bson.M
		if err := bson.Unmarshal(raw, &docM); err != nil {
			panic("Failed to unmarshal alias document: " + err.Error())
		}

		// Merge into our accumulator
		for k, v := range docM {
			fieldMap[k] = v
		}
	}

	return bson.D{{Key: a.lhs.Build(), Value: fieldMap}}
}

func NewComplexAlias(lhs builder.Field, rhs []builder.Alias) builder.Alias {
	return &ComplexAlias{lhs: lhs, rhs: rhs}
}

type aliasesImp struct {
	aliases []builder.Alias
}

func (a *aliasesImp) Field() builder.Field {
	if len(a.aliases) > 0 {
		return a.aliases[0].Field()
	}
	return NewFieldImp("")
}

func (a *aliasesImp) Build() bson.D {
	results := make([]bson.D, 0)
	for _, alias := range a.aliases {
		results = append(results, alias.Build())
	}
	aliases := bson.D{}
	for _, r := range results {
		aliases = append(aliases, r...)
	}
	return aliases
}

func NewAliases(aliases ...builder.Alias) builder.Alias {
	return &aliasesImp{aliases: aliases}
}
