package dialectsfcrm

import (
	"fmt"
	"strings"

	soql "github.com/nexuscrm/dialect-sf-crm-soql"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

func correlatedMembershipFilter(req *models.QueryRequest, filter queryspec.Predicate) queryspec.Predicate {
	if req == nil || len(req.SemiJoins) == 0 {
		return filter
	}
	rewritten := filter
	for _, clause := range req.SemiJoins {
		var ok bool
		rewritten, ok = rewriteSemiJoinPlaceholder(req, rewritten, clause)
		if !ok {
			return queryspec.UnsupportedPredicate(fmt.Sprintf("semi-join placeholder for index %d is missing or malformed", clause.Index))
		}
	}
	return rewritten
}

func rewriteSemiJoinPlaceholder(req *models.QueryRequest, predicate queryspec.Predicate, clause models.SemiJoinClause) (queryspec.Predicate, bool) {
	if isSemiJoinPlaceholderPredicate(predicate, clause) {
		membership, ok := correlatedMembershipPredicate(req, clause)
		return membership, ok
	}

	switch predicate.Kind {
	case queryspec.PredicateAnd, queryspec.PredicateOr:
		children := make([]queryspec.Predicate, len(predicate.Children))
		replaced := false
		for i, child := range predicate.Children {
			rewritten, ok := rewriteSemiJoinPlaceholder(req, child, clause)
			if ok {
				children[i] = rewritten
				replaced = true
			} else {
				children[i] = child
			}
		}
		if !replaced {
			return predicate, false
		}
		copied := predicate
		copied.Children = children
		return copied, true
	case queryspec.PredicateNot:
		if len(predicate.Children) != 1 {
			return predicate, false
		}
		rewritten, ok := rewriteSemiJoinPlaceholder(req, predicate.Children[0], clause)
		if !ok {
			return predicate, false
		}
		copied := predicate
		copied.Children = []queryspec.Predicate{rewritten}
		return copied, true
	default:
		return predicate, false
	}
}

func correlatedMembershipPredicate(req *models.QueryRequest, clause models.SemiJoinClause) (queryspec.Predicate, bool) {
	inner := clause.InnerQuery
	if inner == nil || len(inner.SelectFields) != 1 || inner.ObjectAPIName == "" {
		return queryspec.Predicate{}, false
	}
	return queryspec.CorrelatedMembershipPredicate(queryspec.CorrelatedMembership{
		ID:                 fmt.Sprintf("semi_%d", clause.Index),
		OuterField:         clause.OuterField,
		Negate:             clause.Negate,
		InnerObjectAPIName: inner.ObjectAPIName,
		InnerSelectField:   inner.SelectFields[0],
		InnerFilter:        inner.Filter,
		InnerLimit:         inner.Limit,
		InnerScanAll:       req.ScanAll,
	}), true
}

func isSemiJoinPlaceholderPredicate(predicate queryspec.Predicate, clause models.SemiJoinClause) bool {
	if predicate.Kind != queryspec.PredicateComparison {
		return false
	}
	if predicate.Left.Kind != queryspec.OperandField || !strings.EqualFold(predicate.Left.Field, clause.OuterField) {
		return false
	}

	op := strings.ToLower(strings.Join(strings.Fields(predicate.Op), " "))
	if clause.Negate {
		if op != "not in" {
			return false
		}
	} else if op != "in" {
		return false
	}

	if predicate.Right.Kind != queryspec.OperandList || len(predicate.Right.Elements) != 1 {
		return false
	}
	token, ok := predicate.Right.Elements[0].Value.(string)
	return ok && token == soql.SemiJoinPlaceholderToken(clause.Index)
}
