package zddc

import (
	"reflect"
	"testing"
)

// resolver matching mechanics: literal beats "*", length must match, most-
// specific wins, empty path → root.
func TestPolicyTreeResolve(t *testing.T) {
	tree := PolicyTree{
		"":             {Title: "root"},
		"*":            {Title: "project"},
		"working":      {Title: "literal-working"},
		"*/working":    {Title: "any-working"},
		"*/working/*":  {Title: "any-working-any"},
		"*/mdl":        {Title: "any-mdl"},
		"Proj/working": {Title: "proj-working"},
	}
	cases := []struct {
		segs []string
		want string // resolved key
		ok   bool
	}{
		{nil, "", true},                                     // root
		{[]string{"Proj"}, "*", true},                       // no literal "Proj" at level 1 → "*"
		{[]string{"working"}, "working", true},              // literal beats "*"
		{[]string{"Proj", "working"}, "Proj/working", true}, // both literal beats "*/working"
		{[]string{"Other", "working"}, "*/working", true},   // first seg "*", second literal
		{[]string{"Proj", "mdl"}, "*/mdl", true},
		{[]string{"Proj", "working", "Acme"}, "*/working/*", true},
		{[]string{"Proj", "nope"}, "", false}, // no len-2 key matches "*/nope"
	}
	for _, c := range cases {
		got, ok := tree.resolveTreeDir(c.segs)
		if ok != c.ok || (ok && got != c.want) {
			t.Errorf("resolveTreeDir(%v) = (%q,%v), want (%q,%v)", c.segs, got, ok, c.want, c.ok)
		}
	}
}

// Load the real embedded-default source tree and assert the split content
// reproduces the intended document-control policy (the faithfulness check at
// the data level; full effective-policy parity is the Layer-2 matrix once the
// cascade is wired in Phase 4).
func TestLoadPolicyTreeFromDir(t *testing.T) {
	tree, err := LoadPolicyTreeFromDir("defaults")
	if err != nil {
		t.Fatalf("load defaults tree: %v", err)
	}
	wantKeys := []string{
		"", "*", "*/archive", "*/ssr", "*/incoming", "*/incoming/*",
		"*/reviewing", "*/reviewing/*", "*/working", "*/working/*",
		"*/staging", "*/staging/*", "*/mdl", "*/mdl/*", "*/rsk", "*/rsk/*",
	}
	for _, k := range wantKeys {
		if _, ok := tree[k]; !ok {
			t.Errorf("missing tree key %q", k)
		}
	}
	if len(tree) != len(wantKeys) {
		t.Errorf("tree has %d keys, want %d: %v", len(tree), len(wantKeys), keysOf(tree))
	}

	// Spot-check the policy each member carries.
	if got := tree[""].AvailableTools; !reflect.DeepEqual(got, []string{"archive", "browse", "landing"}) {
		t.Errorf("root available_tools = %v", got)
	}
	if _, ok := tree[""].Roles["document_controller"]; !ok {
		t.Errorf("root missing document_controller role")
	}
	if got := tree["*"].ACL.Permissions["project_team"]; got != "r" {
		t.Errorf("project-level project_team = %q, want r", got)
	}
	if got := tree["*"].ACL.Permissions["document_controller"]; got != "rw" {
		t.Errorf("project-level document_controller = %q, want rw", got)
	}
	if got := tree["*/working"].ACL.Permissions["document_controller"]; got != "rwcda" {
		t.Errorf("working document_controller = %q, want rwcda", got)
	}
	if got := tree["*/working"].ACL.Permissions["project_team"]; got != "cr" {
		t.Errorf("working project_team = %q, want cr", got)
	}
	if tree["*/working"].PartySource != "ssr" {
		t.Errorf("working party_source = %q, want ssr", tree["*/working"].PartySource)
	}
	if h := tree["*/working"].History; h == nil || !*h {
		t.Errorf("working history not true")
	}
	if got := tree["*/mdl"].ACL.Permissions["project_team"]; got != "rwc" {
		t.Errorf("mdl project_team = %q, want rwc", got)
	}
	if got := tree["*/archive"].Worm; !reflect.DeepEqual(got, []string{"document_controller"}) {
		t.Errorf("archive worm = %v, want [document_controller]", got)
	}
	if ao := tree["*/working/*"].AutoOwn; ao == nil || !*ao {
		t.Errorf("working/<party> auto_own not true")
	}
}

// Along returns the governing members in root→leaf order for a path.
func TestPolicyTreeAlong(t *testing.T) {
	tree, err := LoadPolicyTreeFromDir("defaults")
	if err != nil {
		t.Fatalf("load: %v", err)
	}
	levels := tree.Along([]string{"Proj", "working", "Acme"})
	// "", "*", "*/working", "*/working/*" → 4 contributing levels.
	if len(levels) != 4 {
		t.Fatalf("Along returned %d levels, want 4", len(levels))
	}
	// Leaf level is working/<party>: auto_own.
	if ao := levels[3].AutoOwn; ao == nil || !*ao {
		t.Errorf("leaf level should carry auto_own")
	}
	// The working level grants the DC full authority.
	if got := levels[2].ACL.Permissions["document_controller"]; got != "rwcda" {
		t.Errorf("level 2 (working) document_controller = %q, want rwcda", got)
	}
}

func keysOf(t PolicyTree) []string {
	out := make([]string, 0, len(t))
	for k := range t {
		out = append(out, k)
	}
	return out
}
