package commitment

import (
	"testing"

	"github.com/erigontech/erigon/common/dbg"
	"github.com/erigontech/erigon/common/length"
)

func TestDefaultTrieConfig(t *testing.T) {
	cfg := DefaultTrieConfig()

	if cfg.Variant != VariantHexPatriciaTrie {
		t.Errorf("Variant should default to VariantHexPatriciaTrie, got %v", cfg.Variant)
	}
	if !cfg.DeferBranchUpdates {
		t.Error("DeferBranchUpdates should default to true")
	}
	if cfg.LeaveDeferredForCaller {
		t.Error("LeaveDeferredForCaller should default to false")
	}
	if !cfg.EnableTrieWarmup {
		t.Error("EnableTrieWarmup should default to true")
	}
	if cfg.CsvMetricsFilePrefix != "" {
		t.Errorf("CsvMetricsFilePrefix should default to empty, got %q", cfg.CsvMetricsFilePrefix)
	}
	if cfg.MemoizationOff {
		t.Error("MemoizationOff should default to false")
	}
	if cfg.WarmupNumWorkers != 0 {
		t.Errorf("WarmupNumWorkers should default to 0 (use default), got %d", cfg.WarmupNumWorkers)
	}
}

func TestTrieConfig_OrDefaultHelpers(t *testing.T) {
	cfg := TrieConfig{}
	if got := cfg.WarmupNumWorkersOrDefault(); got != dbg.TipTrieWarmupers {
		t.Errorf("WarmupNumWorkersOrDefault: expected %d, got %d", dbg.TipTrieWarmupers, got)
	}

	cfg = TrieConfig{WarmupNumWorkers: 3}
	if got := cfg.WarmupNumWorkersOrDefault(); got != 3 {
		t.Errorf("WarmupNumWorkersOrDefault: expected 3, got %d", got)
	}
}

func TestTrieConfig_WarmupNumWorkers_EnvDisable(t *testing.T) {
	old := dbg.TipTrieWarmupers
	dbg.TipTrieWarmupers = 0
	defer func() { dbg.TipTrieWarmupers = old }()

	if got := (TrieConfig{}).WarmupNumWorkersOrDefault(); got != 0 {
		t.Errorf("explicit TIP_TRIE_WARMUPERS=0 must propagate as 0 (warmup disabled), got %d", got)
	}
}

func TestTrieConfig_Subtrie(t *testing.T) {
	cfg := TrieConfig{
		Variant:                VariantHexPatriciaTrie,
		DeferBranchUpdates:     true,
		LeaveDeferredForCaller: true,
		EnableTrieWarmup:       true,
		CsvMetricsFilePrefix:   "pre",
		MemoizationOff:         true,
		WarmupNumWorkers:       7,
	}

	sub := cfg.Subtrie()

	if sub.DeferBranchUpdates {
		t.Error("Subtrie should disable DeferBranchUpdates")
	}

	want := cfg
	want.DeferBranchUpdates = false
	if sub != want {
		t.Errorf("Subtrie should copy all other fields unchanged: got %+v, want %+v", sub, want)
	}
}

func TestTrieConfig_PropagationToHPH(t *testing.T) {
	cfg := TrieConfig{
		DeferBranchUpdates:     false,
		LeaveDeferredForCaller: true,
		MemoizationOff:         true,
	}

	hph := NewHexPatriciaHashed(length.Addr, nil, cfg)
	defer hph.Release()

	if hph.cfg != cfg {
		t.Error("stored cfg should match what was passed")
	}
	if hph.branchEncoder.deferUpdates {
		t.Error("branchEncoder.deferUpdates should be false")
	}
	if !hph.leaveDeferredForCaller {
		t.Error("leaveDeferredForCaller should be true")
	}
	if !hph.memoizationOff {
		t.Error("memoizationOff should be true")
	}
}

func TestTrieConfig_SpawnSubTrieInheritsConfig(t *testing.T) {
	cfg := TrieConfig{
		DeferBranchUpdates:     true,
		LeaveDeferredForCaller: true,
		MemoizationOff:         true,
	}

	parent := NewHexPatriciaHashed(length.Addr, nil, cfg)
	defer parent.Release()

	sub := parent.SpawnSubTrie(nil, 0)
	defer sub.Release()

	// Sub-trie should inherit config but with DeferBranchUpdates forced to false
	if sub.cfg.DeferBranchUpdates {
		t.Error("sub-trie DeferBranchUpdates should be false")
	}
	if !sub.cfg.LeaveDeferredForCaller {
		t.Error("sub-trie should inherit LeaveDeferredForCaller=true")
	}
	if !sub.cfg.MemoizationOff {
		t.Error("sub-trie should inherit MemoizationOff=true")
	}
	if sub.branchEncoder.deferUpdates {
		t.Error("sub-trie branchEncoder should not defer updates")
	}
}
