package telemetrytraces

import (
	"strings"

	grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar"
	"github.com/antlr4-go/antlr/v4"
)

type traceIDExtractor struct {
	traceIDs []string
	found    bool
}

func ExtractTraceIDsFromFilter(filterExpr string) ([]string, bool) {

	input := antlr.NewInputStream(filterExpr)
	lexer := grammar.NewFilterQueryLexer(input)

	lexer.RemoveErrorListeners()

	tokens := antlr.NewCommonTokenStream(lexer, 0)
	parser := grammar.NewFilterQueryParser(tokens)
	parser.RemoveErrorListeners()

	tree := parser.Query()

	extractor := &traceIDExtractor{}
	extractor.Visit(tree)

	return extractor.traceIDs, extractor.found
}

// Visit dispatches to the specific visit method based on node type.
func (e *traceIDExtractor) Visit(tree antlr.ParseTree) any {
	if tree == nil {
		return nil
	}

	switch t := tree.(type) {
	case *grammar.QueryContext:
		return e.VisitQuery(t)
	case *grammar.ExpressionContext:
		return e.VisitExpression(t)
	case *grammar.OrExpressionContext:
		return e.VisitOrExpression(t)
	case *grammar.AndExpressionContext:
		return e.VisitAndExpression(t)
	case *grammar.UnaryExpressionContext:
		return e.VisitUnaryExpression(t)
	case *grammar.PrimaryContext:
		return e.VisitPrimary(t)
	case *grammar.ComparisonContext:
		return e.VisitComparison(t)
	case *grammar.InClauseContext:
		return e.VisitInClause(t)
	default:
		// For other node types, visit children
		for i := 0; i < tree.GetChildCount(); i++ {
			if child := tree.GetChild(i); child != nil {
				if parseTree, ok := child.(antlr.ParseTree); ok {
					e.Visit(parseTree)
				}
			}
		}
	}
	return nil
}

func (e *traceIDExtractor) VisitQuery(ctx *grammar.QueryContext) any {
	return e.Visit(ctx.Expression())
}

func (e *traceIDExtractor) VisitExpression(ctx *grammar.ExpressionContext) any {
	return e.Visit(ctx.OrExpression())
}

func (e *traceIDExtractor) VisitOrExpression(ctx *grammar.OrExpressionContext) any {
	for _, expr := range ctx.AllAndExpression() {
		e.Visit(expr)
	}
	return nil
}

func (e *traceIDExtractor) VisitAndExpression(ctx *grammar.AndExpressionContext) any {
	for _, expr := range ctx.AllUnaryExpression() {
		e.Visit(expr)
	}
	return nil
}

func (e *traceIDExtractor) VisitUnaryExpression(ctx *grammar.UnaryExpressionContext) any {
	return e.Visit(ctx.Primary())
}

func (e *traceIDExtractor) VisitPrimary(ctx *grammar.PrimaryContext) any {
	if ctx.OrExpression() != nil {
		return e.Visit(ctx.OrExpression())
	} else if ctx.Comparison() != nil {
		return e.Visit(ctx.Comparison())
	}
	return nil
}

func (e *traceIDExtractor) VisitComparison(ctx *grammar.ComparisonContext) any {
	keyCtx := ctx.Key()
	if keyCtx == nil {
		return nil
	}

	keyText := keyCtx.GetText()

	if strings.ToLower(keyText) == "trace_id" || strings.ToLower(keyText) == "traceid" {
		if ctx.EQUALS() != nil {
			values := ctx.AllValue()
			if len(values) > 0 {
				if value := e.extractValue(values[0]); value != "" {
					e.traceIDs = append(e.traceIDs, value)
					e.found = true
				}
			}
		} else if ctx.InClause() != nil {
			return e.Visit(ctx.InClause())
		}
	}

	return nil
}

func (e *traceIDExtractor) VisitInClause(ctx *grammar.InClauseContext) any {
	valueListCtx := ctx.ValueList()
	if valueListCtx == nil {
		return nil
	}

	for _, valueCtx := range valueListCtx.AllValue() {
		if value := e.extractValue(valueCtx); value != "" {
			e.traceIDs = append(e.traceIDs, value)
			e.found = true
		}
	}

	return nil
}

func (e *traceIDExtractor) extractValue(ctx grammar.IValueContext) string {
	if ctx.QUOTED_TEXT() != nil {
		text := ctx.QUOTED_TEXT().GetText()
		if len(text) >= 2 {
			return text[1 : len(text)-1]
		}
	} else if ctx.KEY() != nil {
		return ctx.KEY().GetText()
	} else if ctx.NUMBER() != nil {
		return ctx.NUMBER().GetText()
	}
	return ""
}
