package builderimp

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

type condImp struct {
	condition builder.Expression
	ifTrue    any
	ifFalse   any
}

func (c *condImp) Build() any {
	return bson.D{
		{Key: string(builder.Cond), Value: bson.D{
			{Key: "if", Value: c.condition.Build()},
			{Key: "then", Value: c.ifTrue},
			{Key: "else", Value: c.ifFalse},
		}},
	}
}

func NewCond(condition builder.Expression, ifTrue, ifFalse any) builder.Expression {
	return &condImp{
		condition: condition,
		ifTrue:    ifTrue,
		ifFalse:   ifFalse,
	}
}

// setUnionImp implements builder.Expression but takes only builder.Array inputs.
type setUnionImp struct {
	inputs []builder.Expression
}

// Build renders the $setUnion stage:
//
//	{ $setUnion: [ <array1>, <array2>, … ] }
func (s *setUnionImp) Build() any {
	arr := make(bson.A, len(s.inputs))
	for i, arrayExpr := range s.inputs {
		arr[i] = arrayExpr.Build()
	}
	return bson.D{
		{Key: string(builder.SetUnion), Value: arr},
	}
}

// NewSetUnion constructs a new $setUnion expression from the given Arrays.
func NewSetUnion(arrays ...builder.Expression) builder.Expression {
	return &setUnionImp{inputs: arrays}
}

type assignmentImp struct {
	field      builder.Field
	expression builder.Expression
}

func (a *assignmentImp) Build() bson.D {
	// Assign it to the given field name
	return bson.D{
		{Key: a.field.Build(), Value: a.expression.Build()},
	}
}

// NewAssignment creates a projection assignment of the form:
//
//	<field>: <expression>
func NewAssignment(field builder.Field, expression builder.Expression) builder.Projection {
	return &assignmentImp{
		field:      field,
		expression: expression,
	}
}

type computeImp struct {
	field      builder.Field
	expression builder.Expression
}

func (a *computeImp) Build() any {
	return bson.D{
		{Key: a.field.Build(), Value: a.expression.Build()},
	}
}

func NewCompute(field builder.Field, expression builder.Expression) builder.Expression {
	return &computeImp{
		field:      field,
		expression: expression,
	}
}

func NewIfNull(expression, replacement builder.Expression) builder.Expression {
	return newBinaryExpression(builder.IfNull, expression, replacement)
}

func NewPush(expression builder.Expression) builder.Expression {
	return newUnaryExpression(builder.Push, expression)
}

func NewAnd(exprs ...builder.Expression) builder.Expression {
	return newVariadicExpression(builder.And, exprs...)
}

func NewOr(exprs ...builder.Expression) builder.Expression {
	return newVariadicExpression(builder.Or, exprs...)
}

func NewEach(exprs ...builder.Expression) builder.Expression {
	return newVariadicExpression(builder.Each, exprs...)
}

func NewLt(left, right builder.Expression) builder.Expression {
	return newBinaryExpression(builder.Lt, left, right)
}

func NewNot(expression builder.Expression) builder.Expression {
	return newUnaryExpression(builder.Not, expression)
}

func NewSum(expression builder.Expression) builder.Expression {
	return newUnaryExpression(builder.Sum, expression)
}

func NewMin(expression builder.Expression) builder.Expression {
	return newUnaryExpression(builder.Min, expression)
}

func First(expr builder.Expression) builder.Expression {
	return newUnaryExpression(builder.First, expr)
}

func NewType(expr builder.Expression) builder.Expression {
	return newUnaryExpression(builder.Type, expr)
}
