package zddc

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestEnsureCanonicalAncestors_LazyCreation(t *testing.T) {
	root := t.TempDir()
	// working/ is a top-level peer; its <party> folder auto-owns the
	// creator (unfenced — party admins still cascade in). Per-user email
	// homes were abandoned in the reshape.
	target := filepath.Join(root, "Proj", "working", "ACME", "notes.md")

	resolved, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755)
	if err != nil {
		t.Fatalf("ensure: %v", err)
	}
	if resolved != target {
		t.Errorf("resolved=%q, target=%q (no case variant exists, should be identical)", resolved, target)
	}

	autoZ := filepath.Join(root, "Proj", "working", "ACME", ".zddc")
	data, err := os.ReadFile(autoZ)
	if err != nil {
		t.Fatalf("auto-own .zddc not written at working/ACME/: %v", err)
	}
	body := string(data)
	if !strings.Contains(body, "alice@x.com: rwcda") {
		t.Errorf("auto-own grant missing: %s", body)
	}
	if !strings.Contains(body, "created_by: alice@x.com") {
		t.Errorf("created_by missing: %s", body)
	}
	if strings.Contains(body, "inherit: false") {
		t.Errorf("working/<party>/ .zddc should be UNFENCED; got: %s", body)
	}
	// The working/ peer root itself does NOT auto-own (auto_own is at the
	// <party> level).
	if _, err := os.Stat(filepath.Join(root, "Proj", "working", ".zddc")); !os.IsNotExist(err) {
		t.Errorf("working/ peer root should not have auto-own .zddc; got err=%v", err)
	}
}

// staging/<party>/<folder>/ is NOT auto-owned — only the <party> level is.
func TestEnsureCanonicalAncestors_StagingChildNotFenced(t *testing.T) {
	root := t.TempDir()
	target := filepath.Join(root, "Proj", "staging", "ACME",
		"2025-10-31_proj-EM-TRN-0042 (RSA) - Outbound", "doc.pdf")
	if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err != nil {
		t.Fatalf("ensure: %v", err)
	}
	childZddc := filepath.Join(root, "Proj", "staging", "ACME",
		"2025-10-31_proj-EM-TRN-0042 (RSA) - Outbound", ".zddc")
	if _, err := os.Stat(childZddc); !os.IsNotExist(err) {
		t.Errorf("staging child should NOT have auto-own .zddc; got err=%v", err)
	}
	stagingZddc := filepath.Join(root, "Proj", "staging", "ACME", ".zddc")
	if _, err := os.Stat(stagingZddc); err != nil {
		t.Errorf("staging/<party> auto-own .zddc missing: %v", err)
	}
}

func TestEnsureCanonicalAncestors_CaseFoldReuse(t *testing.T) {
	root := t.TempDir()
	// Pre-create Archive/ (PascalCase) — case-fold reuse applies to the
	// canonical project-root peer + the received/issued slots.
	if err := os.MkdirAll(filepath.Join(root, "Proj", "Archive", "ACME", "received"), 0o755); err != nil {
		t.Fatal(err)
	}

	target := filepath.Join(root, "Proj", "archive", "ACME", "received", "foo.pdf")
	resolved, err := EnsureCanonicalAncestors(root, target, "dc@x.com", 0o755)
	if err != nil {
		t.Fatalf("ensure: %v", err)
	}
	want := filepath.Join(root, "Proj", "Archive", "ACME", "received", "foo.pdf")
	if resolved != want {
		t.Errorf("resolved=%q, want %q", resolved, want)
	}
	if _, err := os.Stat(filepath.Join(root, "Proj", "archive")); !os.IsNotExist(err) {
		t.Errorf("lowercase sibling should not exist; got err=%v", err)
	}
}

func TestEnsureCanonicalAncestors_PerPartyIncoming(t *testing.T) {
	root := t.TempDir()
	// incoming/ is a top-level peer; its <party> folder auto-owns.
	target := filepath.Join(root, "Proj", "incoming", "ACME", "submission.pdf")

	if _, err := EnsureCanonicalAncestors(root, target, "rep@acme.com", 0o755); err != nil {
		t.Fatalf("ensure: %v", err)
	}
	autoZ := filepath.Join(root, "Proj", "incoming", "ACME", ".zddc")
	data, err := os.ReadFile(autoZ)
	if err != nil {
		t.Fatalf("auto-own .zddc at incoming/ACME/ not written: %v", err)
	}
	if !strings.Contains(string(data), "rep@acme.com: rwcda") {
		t.Errorf("incoming/ACME auto-own missing rep grant: %s", data)
	}
	if strings.Contains(string(data), "inherit: false") {
		t.Errorf("incoming/ACME auto-own should be UNFENCED; got: %s", data)
	}
}

func TestEnsureCanonicalAncestors_WormFoldersNoAutoOwn(t *testing.T) {
	root := t.TempDir()
	target := filepath.Join(root, "Proj", "archive", "ACME", "issued", "spec.pdf")

	if _, err := EnsureCanonicalAncestors(root, target, "dc@mycompany.com", 0o755); err != nil {
		t.Fatalf("ensure: %v", err)
	}
	if _, err := os.Stat(filepath.Join(root, "Proj", "archive", "ACME", "issued")); err != nil {
		t.Errorf("issued/ not created: %v", err)
	}
	if _, err := os.Stat(filepath.Join(root, "Proj", "archive", "ACME", "issued", ".zddc")); !os.IsNotExist(err) {
		t.Errorf("issued/ should NOT have auto-own .zddc (WORM); got err=%v", err)
	}
	// archive/ and archive/<party>/ are plain containers — no auto-own.
	if _, err := os.Stat(filepath.Join(root, "Proj", "archive", ".zddc")); !os.IsNotExist(err) {
		t.Errorf("archive/ should not have auto-own .zddc; got err=%v", err)
	}
}

func TestEnsureCanonicalAncestors_NoPrincipalSkipsAutoOwn(t *testing.T) {
	root := t.TempDir()
	target := filepath.Join(root, "Proj", "working", "ACME", "anon.md")

	if _, err := EnsureCanonicalAncestors(root, target, "" /* no email */, 0o755); err != nil {
		t.Fatalf("ensure: %v", err)
	}
	if _, err := os.Stat(filepath.Join(root, "Proj", "working", "ACME")); err != nil {
		t.Errorf("working/ACME not created: %v", err)
	}
	if _, err := os.Stat(filepath.Join(root, "Proj", "working", "ACME", ".zddc")); !os.IsNotExist(err) {
		t.Errorf("auto-own .zddc must not be written without principalEmail; got err=%v", err)
	}
}

// Top-level peers are physical now — a write under <project>/<peer>/<party>/
// is created normally (no virtual-aggregator rejection).
func TestEnsureCanonicalAncestors_TopLevelPeersCreated(t *testing.T) {
	root := t.TempDir()
	for _, peer := range []string{"working", "staging", "reviewing", "incoming", "mdl", "rsk", "ssr"} {
		target := filepath.Join(root, "Proj", peer, "ACME", "x.md")
		if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err != nil {
			t.Errorf("%s: unexpected error: %v", peer, err)
		}
		if _, err := os.Stat(filepath.Join(root, "Proj", peer, "ACME")); err != nil {
			t.Errorf("%s: <project>/%s/ACME/ should be created; got err=%v", peer, peer, err)
		}
	}
}

func TestEnsureCanonicalAncestors_RejectsTraversal(t *testing.T) {
	root := t.TempDir()
	other := t.TempDir()
	target := filepath.Join(other, "evil.md")
	if _, err := EnsureCanonicalAncestors(root, target, "alice@x.com", 0o755); err == nil {
		t.Errorf("expected error for target outside fsRoot")
	}
}
