package analytics

import (
	"strings"
	"testing"
)

func TestCompileFlatDivisionPreservesFenceAndNullProvenance(t *testing.T) {
	ast, err := Parse(`IF(Num__c/Den__c/Alt__c > 1, 10, 20)`)
	if err != nil {
		t.Fatalf("Parse() error = %v", err)
	}

	got, err := Compile(ast, &CompileContext{
		TableAlias:    "t",
		QualifyFields: true,
		JoinAliases:   map[string]string{},
		FieldTypes: map[string]string{
			"Num__c": "double",
			"Den__c": "double",
			"Alt__c": "double",
		},
	})
	if err != nil {
		t.Fatalf("Compile() error = %v", err)
	}

	want := "CASE (((CAST((CAST(COALESCE(t.`Num__c`, 0) AS DECIMAL(65,30)) / " +
		"CAST(COALESCE(t.`Den__c`, 0) AS DECIMAL(65,30))) AS DECIMAL(65,30)) / " +
		"CAST(COALESCE(t.`Alt__c`, 0) AS DECIMAL(65,30))) > 1)) " +
		"WHEN TRUE THEN 10 WHEN FALSE THEN 20 END"
	if got != want {
		t.Fatalf("flat nested-division emission drifted:\n got: %s\nwant: %s", got, want)
	}
}

func TestCompileLongFlatDivisionChainIsSinglePass(t *testing.T) {
	// parseMulDiv builds a left-associative tree in a loop. Twice the parser's
	// recursive nesting limit is therefore a valid input and guards the separate
	// flat-chain path that previously rescanned and recompiled every prefix.
	const terms = 2 * maxParseDepth
	source := "IF(Num__c" + strings.Repeat("/Den__c", terms-1) + " > 1, 10, 20)"
	ast, err := Parse(source)
	if err != nil {
		t.Fatalf("Parse(%d-term flat chain) error = %v", terms, err)
	}

	astNodes := 0
	walkAST(ast, func(Node) { astNodes++ })
	work := &formulaCompileWork{}
	got, err := Compile(ast, &CompileContext{
		TableAlias:    "t",
		QualifyFields: true,
		JoinAliases:   map[string]string{},
		FieldTypes: map[string]string{
			"Num__c": "double",
			"Den__c": "double",
		},
		compileWork: work,
	})
	if err != nil {
		t.Fatalf("Compile(%d-term flat chain) error = %v", terms, err)
	}

	if work.analyzedNodes != astNodes {
		t.Fatalf("division analysis visited %d nodes, want one visit for each of %d AST nodes",
			work.analyzedNodes, astNodes)
	}
	if work.compiledNodes != astNodes {
		t.Fatalf("compiler emitted %d AST nodes, want one pass over %d nodes; a larger count means a prefix was recompiled",
			work.compiledNodes, astNodes)
	}

	divisions := terms - 1
	if gotCount := strings.Count(got, " / "); gotCount != divisions {
		t.Fatalf("compiled division count = %d, want %d", gotCount, divisions)
	}
	if gotCount := strings.Count(got, " AS DECIMAL(65,30))"); gotCount != 2*divisions {
		t.Fatalf("nested-division fence count = %d, want %d", gotCount, 2*divisions)
	}
	if gotCount := strings.Count(got, nullStrictGuardMarker); gotCount != 1 {
		t.Fatalf("strict NULL-provenance marker count = %d, want 1", gotCount)
	}
}
