package builtin

import (
	"context"
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/denn-gubsky/loomcycle/internal/config"
	"github.com/denn-gubsky/loomcycle/internal/store"
	"github.com/denn-gubsky/loomcycle/internal/store/sqlite"
	"github.com/denn-gubsky/loomcycle/internal/tools"
)

// volumeDefFixture builds a VolumeDef tool over in-memory SQLite + a config
// with a dynamic_root pointing at a real temp dir. Returns the tool, an
// "any"-scope ctx for the shared tenant, the resolved dynamic root path,
// and cleanup. scopes lets a test override the volume_def_scopes grant.
func volumeDefFixture(t *testing.T, scopes ...string) (*VolumeDef, context.Context, string, func()) {
	t.Helper()
	s, err := sqlite.Open(":memory:")
	if err != nil {
		t.Fatalf("sqlite.Open: %v", err)
	}
	// EvalSymlinks so the derived/resolved paths match on macOS (/var ->
	// /private/var), where the purge fence's EvalSymlinks would otherwise
	// see a different real path than the configured one.
	root, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	cfg := &config.Config{
		Volumes: map[string]config.Volume{
			"pool":   {Path: root, Mode: "rw", DynamicRoot: true},
			"static": {Path: root, Mode: "ro"}, // a non-root static volume (ground-truth collision test)
		},
	}
	tool := &VolumeDef{Store: s, Cfg: cfg, MaxNameLen: 64}
	if len(scopes) == 0 {
		scopes = []string{"any"}
	}
	ctx := tools.WithRunIdentity(context.Background(), tools.RunIdentityValue{AgentID: "a_admin"})
	ctx = tools.WithVolumeDefPolicy(ctx, tools.VolumeDefPolicyValue{Scopes: scopes})
	return tool, ctx, root, func() { _ = s.Close() }
}

func vdExec(t *testing.T, tool *VolumeDef, ctx context.Context, body string) (map[string]any, tools.Result) {
	t.Helper()
	res, err := tool.Execute(ctx, json.RawMessage(body))
	if err != nil {
		t.Fatalf("Execute(%s): %v", body, err)
	}
	if res.IsError {
		return nil, res
	}
	var out map[string]any
	if err := json.Unmarshal([]byte(res.Text), &out); err != nil {
		t.Fatalf("decode %q: %v", res.Text, err)
	}
	return out, res
}

// create derives <root>/_shared/<name> and mkdirs it.
func TestVolumeDefTool_CreateDerivesPathAndMkdirs(t *testing.T) {
	tool, ctx, root, cleanup := volumeDefFixture(t)
	defer cleanup()

	out, res := vdExec(t, tool, ctx, `{"op":"create","name":"repo-a","mode":"rw"}`)
	if res.IsError {
		t.Fatalf("create: %s", res.Text)
	}
	want := filepath.Join(root, "_shared", "repo-a")
	if out["path"] != want {
		t.Errorf("path = %v, want %v", out["path"], want)
	}
	if out["mode"] != "rw" {
		t.Errorf("mode = %v, want rw", out["mode"])
	}
	if info, err := os.Stat(want); err != nil || !info.IsDir() {
		t.Errorf("directory not created at %q: err=%v", want, err)
	}
}

// A tenant create lands under <root>/<tenant>/<name>.
func TestVolumeDefTool_CreateTenantSegment(t *testing.T) {
	tool, baseCtx, root, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx := tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{AgentID: "a", TenantID: "acme"})
	ctx = tools.WithVolumeDefPolicy(ctx, tools.VolumeDefPolicyValue{Scopes: []string{"any"}})

	out, res := vdExec(t, tool, ctx, `{"op":"create","name":"work"}`)
	if res.IsError {
		t.Fatalf("create: %s", res.Text)
	}
	want := filepath.Join(root, "acme", "work")
	if out["path"] != want {
		t.Errorf("path = %v, want %v", out["path"], want)
	}
	if out["mode"] != "rw" { // default mode
		t.Errorf("default mode = %v, want rw", out["mode"])
	}
}

// create refuses a bad name (path injection attempts).
func TestVolumeDefTool_CreateRefusesBadName(t *testing.T) {
	tool, ctx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	for _, bad := range []string{"../x", "a/b", ".x", "Repo", "with space", ""} {
		body := `{"op":"create","name":` + mustJSON(bad) + `}`
		_, res := vdExec(t, tool, ctx, body)
		if !res.IsError {
			t.Errorf("name %q should be refused", bad)
		}
	}
}

// create refuses a name colliding with a static volume (yaml is ground truth).
func TestVolumeDefTool_CreateRefusesStaticCollision(t *testing.T) {
	tool, ctx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	_, res := vdExec(t, tool, ctx, `{"op":"create","name":"static"}`)
	if !res.IsError || !strings.Contains(res.Text, "ground truth") {
		t.Errorf("static collision should be refused, got: %s", res.Text)
	}
}

// create refuses when no dynamic_root is configured.
func TestVolumeDefTool_CreateRefusesNoDynamicRoot(t *testing.T) {
	s, err := sqlite.Open(":memory:")
	if err != nil {
		t.Fatal(err)
	}
	defer s.Close()
	root, _ := filepath.EvalSymlinks(t.TempDir())
	cfg := &config.Config{Volumes: map[string]config.Volume{
		"plain": {Path: root, Mode: "rw"}, // no dynamic_root
	}}
	tool := &VolumeDef{Store: s, Cfg: cfg}
	ctx := tools.WithVolumeDefPolicy(tools.WithRunIdentity(context.Background(), tools.RunIdentityValue{}), tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	_, res := vdExec(t, tool, ctx, `{"op":"create","name":"x"}`)
	if !res.IsError || !strings.Contains(res.Text, "no dynamic volume root configured") {
		t.Errorf("expected no-dynamic-root refusal, got: %s", res.Text)
	}
}

// Idempotent: identical re-create is a no-op-OK; a different mode updates it.
func TestVolumeDefTool_CreateIdempotentAndUpdatesMode(t *testing.T) {
	tool, ctx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"data","mode":"rw"}`); res.IsError {
		t.Fatalf("create: %s", res.Text)
	}
	out, res := vdExec(t, tool, ctx, `{"op":"create","name":"data","mode":"rw"}`)
	if res.IsError {
		t.Fatalf("re-create identical: %s", res.Text)
	}
	if out["mode"] != "rw" {
		t.Errorf("mode = %v", out["mode"])
	}
	// Different mode updates.
	out, res = vdExec(t, tool, ctx, `{"op":"create","name":"data","mode":"ro"}`)
	if res.IsError || out["mode"] != "ro" {
		t.Errorf("mode update failed: %v %s", out, res.Text)
	}
	// Still exactly one row.
	listOut, _ := vdExec(t, tool, ctx, `{"op":"list"}`)
	if vols, _ := listOut["volumes"].([]any); len(vols) != 1 {
		t.Errorf("update minted a new row: %d volumes", len(vols))
	}
}

// delete removes the row but LEAVES files on disk.
func TestVolumeDefTool_DeleteLeavesFiles(t *testing.T) {
	tool, ctx, root, cleanup := volumeDefFixture(t)
	defer cleanup()
	vdExec(t, tool, ctx, `{"op":"create","name":"keep","mode":"rw"}`)
	dir := filepath.Join(root, "_shared", "keep")
	marker := filepath.Join(dir, "file.txt")
	if err := os.WriteFile(marker, []byte("data"), 0o600); err != nil {
		t.Fatal(err)
	}

	out, res := vdExec(t, tool, ctx, `{"op":"delete","name":"keep"}`)
	if res.IsError {
		t.Fatalf("delete: %s", res.Text)
	}
	if out["files_removed"] != false {
		t.Errorf("delete should not remove files: %v", out)
	}
	if _, err := os.Stat(marker); err != nil {
		t.Errorf("delete removed the files (should leave them): %v", err)
	}
	// Row is gone.
	if _, res := vdExec(t, tool, ctx, `{"op":"get","name":"keep"}`); !res.IsError {
		t.Error("row still present after delete")
	}
}

// purge removes the row AND the directory.
func TestVolumeDefTool_PurgeRemovesRowAndFiles(t *testing.T) {
	tool, ctx, root, cleanup := volumeDefFixture(t)
	defer cleanup()
	vdExec(t, tool, ctx, `{"op":"create","name":"gone","mode":"rw"}`)
	dir := filepath.Join(root, "_shared", "gone")
	if err := os.WriteFile(filepath.Join(dir, "f.txt"), []byte("x"), 0o600); err != nil {
		t.Fatal(err)
	}

	out, res := vdExec(t, tool, ctx, `{"op":"purge","name":"gone"}`)
	if res.IsError {
		t.Fatalf("purge: %s", res.Text)
	}
	if out["files_removed"] != true {
		t.Errorf("purge should remove files: %v", out)
	}
	if _, err := os.Stat(dir); !os.IsNotExist(err) {
		t.Errorf("purge left the directory: err=%v", err)
	}
	if _, res := vdExec(t, tool, ctx, `{"op":"get","name":"gone"}`); !res.IsError {
		t.Error("row still present after purge")
	}
}

// THE FENCE TEST. A row whose stored definition.path was tampered to escape
// the dynamic root must NOT cause purge to delete the tampered target —
// purge RE-DERIVES the path from (root, tenant, name) and only ever
// RemoveAll's the derived path. We assert the escape target survives.
//
// FAIL-BEFORE: if purge trusted the stored path (os.RemoveAll(body.Path)),
// the escape directory below would be deleted and this test would fail.
func TestVolumeDefTool_PurgeReDerivesIgnoringTamperedPath(t *testing.T) {
	tool, ctx, root, cleanup := volumeDefFixture(t)
	defer cleanup()

	// A directory OUTSIDE the dynamic root that an attacker would target.
	outside, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	escapeFile := filepath.Join(outside, "precious.txt")
	if err := os.WriteFile(escapeFile, []byte("do not delete"), 0o600); err != nil {
		t.Fatal(err)
	}

	// Create the volume legitimately, then tamper the stored path directly
	// in the store to point at `outside`.
	vdExec(t, tool, ctx, `{"op":"create","name":"evil","mode":"rw"}`)
	tampered, _ := json.Marshal(volumeDefBody{Path: outside, Mode: "rw"})
	if _, err := tool.Store.VolumeDefCreate(ctx, store.VolumeDefRow{TenantID: "", Name: "evil", Definition: tampered}); err != nil {
		t.Fatal(err)
	}

	// The derived (real) directory still exists from create.
	derived := filepath.Join(root, "_shared", "evil")
	if _, err := os.Stat(derived); err != nil {
		t.Fatalf("derived dir missing: %v", err)
	}

	out, res := vdExec(t, tool, ctx, `{"op":"purge","name":"evil"}`)
	if res.IsError {
		t.Fatalf("purge: %s", res.Text)
	}
	// The DERIVED dir is gone; the TAMPERED escape target is untouched.
	if _, err := os.Stat(derived); !os.IsNotExist(err) {
		t.Errorf("purge did not remove the derived dir: %v", err)
	}
	if _, err := os.Stat(escapeFile); err != nil {
		t.Errorf("FENCE BREACH: purge followed the tampered path and deleted %q: %v", escapeFile, err)
	}
	_ = out
}

// Tenant isolation: tenant A cannot get/delete/purge tenant B's volume.
func TestVolumeDefTool_TenantIsolationOpaque404(t *testing.T) {
	tool, baseCtx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctxA := tools.WithVolumeDefPolicy(tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{TenantID: "a"}), tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	ctxB := tools.WithVolumeDefPolicy(tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{TenantID: "b"}), tools.VolumeDefPolicyValue{Scopes: []string{"any"}})

	vdExec(t, tool, ctxA, `{"op":"create","name":"secret","mode":"rw"}`)

	// B cannot get / delete / purge A's volume → opaque not-found.
	for _, op := range []string{`{"op":"get","name":"secret"}`, `{"op":"delete","name":"secret"}`, `{"op":"purge","name":"secret"}`} {
		_, res := vdExec(t, tool, ctxB, op)
		if !res.IsError || !strings.Contains(res.Text, "not found") {
			t.Errorf("B op %s should be opaque-404, got: %s", op, res.Text)
		}
	}
	// A's volume still resolves for A.
	if _, res := vdExec(t, tool, ctxA, `{"op":"get","name":"secret"}`); res.IsError {
		t.Errorf("A lost its own volume: %s", res.Text)
	}
}

// Capability gate: default-deny on empty scopes; named:x allows only x; any allows all.
func TestVolumeDefTool_CapabilityGate(t *testing.T) {
	// Empty scopes → default-deny on create/delete/purge.
	tool, baseCtx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	denyCtx := tools.WithVolumeDefPolicy(tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{}), tools.VolumeDefPolicyValue{})
	for _, op := range []string{`{"op":"create","name":"x"}`, `{"op":"delete","name":"x"}`, `{"op":"purge","name":"x"}`} {
		_, res := vdExec(t, tool, denyCtx, op)
		if !res.IsError || !strings.Contains(res.Text, "default-deny") {
			t.Errorf("empty scopes should default-deny %s, got: %s", op, res.Text)
		}
	}

	// named:allowed permits "allowed" but refuses "other".
	namedCtx := tools.WithVolumeDefPolicy(tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{}), tools.VolumeDefPolicyValue{Scopes: []string{"named:allowed"}})
	if _, res := vdExec(t, tool, namedCtx, `{"op":"create","name":"allowed"}`); res.IsError {
		t.Errorf("named:allowed should permit create allowed: %s", res.Text)
	}
	if _, res := vdExec(t, tool, namedCtx, `{"op":"create","name":"other"}`); !res.IsError {
		t.Error("named:allowed should refuse create other")
	}
	// The gate applies to delete + purge too — not just create.
	for _, op := range []string{`{"op":"delete","name":"other"}`, `{"op":"purge","name":"other"}`} {
		if _, res := vdExec(t, tool, namedCtx, op); !res.IsError {
			t.Errorf("named:allowed should refuse %s on other", op)
		}
	}
}

// A tenant whose minted id is literally the reserved shared segment is refused
// on every op — it would otherwise collide on-disk with the shared tenant
// (one could purge the other's volume tree). Operator-gated, cheaply fenced.
func TestVolumeDefTool_ReservedTenantRefused(t *testing.T) {
	tool, baseCtx, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx := tools.WithVolumeDefPolicy(
		tools.WithRunIdentity(baseCtx, tools.RunIdentityValue{TenantID: "_shared"}),
		tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	for _, op := range []string{`{"op":"create","name":"x"}`, `{"op":"purge","name":"x"}`, `{"op":"list"}`} {
		if _, res := vdExec(t, tool, ctx, op); !res.IsError || !strings.Contains(res.Text, "reserved") {
			t.Errorf("reserved tenant must be refused on %s; got %s", op, res.Text)
		}
	}
}

func mustJSON(s string) string {
	b, _ := json.Marshal(s)
	return string(b)
}

// ---- RFC AH Phase 2b ephemeral volume tool tests ----

// ephemeralCtx derives an ephemeral-capable ctx (RootRunID + a fresh
// EphemeralVolumeSet) from the fixture's base ctx.
func ephemeralCtx(base context.Context, rootRunID string) (context.Context, *tools.EphemeralVolumeSet) {
	set := tools.NewEphemeralVolumeSet()
	ctx := tools.WithRunIdentity(base, tools.RunIdentityValue{AgentID: "a", RootRunID: rootRunID})
	ctx = tools.WithVolumeDefPolicy(ctx, tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	ctx = tools.WithEphemeralVolumes(ctx, set)
	return ctx, set
}

// create ephemeral derives <root>/_ephemeral/<run>/<name>, mkdirs it, and
// registers it in the run-scoped set.
func TestVolumeDefTool_CreateEphemeralDerivesPathAndRegisters(t *testing.T) {
	tool, base, root, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx, set := ephemeralCtx(base, "run-xyz")

	out, res := vdExec(t, tool, ctx, `{"op":"create","name":"work","mode":"rw","ephemeral":true}`)
	if res.IsError {
		t.Fatalf("create ephemeral: %s", res.Text)
	}
	want := filepath.Join(root, "_ephemeral", "run-xyz", "work")
	if out["path"] != want {
		t.Errorf("path = %v, want %v", out["path"], want)
	}
	if out["ephemeral"] != true {
		t.Errorf("ephemeral flag = %v, want true", out["ephemeral"])
	}
	if info, err := os.Stat(want); err != nil || !info.IsDir() {
		t.Errorf("ephemeral directory not created at %q: err=%v", want, err)
	}
	// Registered in the run-scoped set for resolution.
	ref, ok := set.Get("work")
	if !ok || ref.Root != want || ref.ReadOnly {
		t.Errorf("set.Get(work) = %+v ok=%v, want root=%q ro=false", ref, ok, want)
	}
}

// create ephemeral refuses when no RootRunID is on ctx (no active run).
func TestVolumeDefTool_CreateEphemeralRefusesWithoutRootRun(t *testing.T) {
	tool, base, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	// RootRunID="" but a set IS attached — the missing run id is the refusal.
	ctx := tools.WithRunIdentity(base, tools.RunIdentityValue{AgentID: "a"})
	ctx = tools.WithVolumeDefPolicy(ctx, tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	ctx = tools.WithEphemeralVolumes(ctx, tools.NewEphemeralVolumeSet())
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"work","ephemeral":true}`); !res.IsError || !strings.Contains(res.Text, "active run") {
		t.Errorf("ephemeral create without root run id must refuse; got %s", res.Text)
	}
}

// create ephemeral refuses a duplicate name within the SAME run tree, but a
// DIFFERENT run's identically-named ephemeral volume is fine (run-scoped).
func TestVolumeDefTool_CreateEphemeralRefusesDuplicateInRun(t *testing.T) {
	tool, base, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx1, _ := ephemeralCtx(base, "run-1")
	if _, res := vdExec(t, tool, ctx1, `{"op":"create","name":"work","ephemeral":true}`); res.IsError {
		t.Fatalf("first create: %s", res.Text)
	}
	if _, res := vdExec(t, tool, ctx1, `{"op":"create","name":"work","ephemeral":true}`); !res.IsError || !strings.Contains(res.Text, "already exists") {
		t.Errorf("duplicate ephemeral name in same run must refuse; got %s", res.Text)
	}
	// A different run can create the same name — fresh set, no collision.
	ctx2, _ := ephemeralCtx(base, "run-2")
	if _, res := vdExec(t, tool, ctx2, `{"op":"create","name":"work","ephemeral":true}`); res.IsError {
		t.Errorf("different run's ephemeral `work` must succeed; got %s", res.Text)
	}
}

// create ephemeral refuses a name colliding with a static volume (static is
// ground truth — an ephemeral volume must never shadow it).
func TestVolumeDefTool_CreateEphemeralRefusesStaticCollision(t *testing.T) {
	tool, base, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx, _ := ephemeralCtx(base, "run-1")
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"static","ephemeral":true}`); !res.IsError || !strings.Contains(res.Text, "static volumes") {
		t.Errorf("ephemeral name colliding with a static volume must refuse; got %s", res.Text)
	}
}

// create ephemeral refuses a name colliding with a PERSISTENT DYNAMIC volume —
// else the ephemeral (resolved first by effectiveRoot) would silently shadow
// the durable one and writes meant for it would be purged at run end.
func TestVolumeDefTool_CreateEphemeralRefusesPersistentDynamicCollision(t *testing.T) {
	tool, base, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx, _ := ephemeralCtx(base, "run-1")
	// A persistent dynamic volume "durable" in the same (shared) tenant.
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"durable","mode":"rw"}`); res.IsError {
		t.Fatalf("persistent create: %s", res.Text)
	}
	// An ephemeral volume of the same name must be refused (would shadow it).
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"durable","ephemeral":true}`); !res.IsError || !strings.Contains(res.Text, "collides") {
		t.Errorf("ephemeral name colliding with a persistent dynamic volume must refuse; got %s", res.Text)
	}
}

// the reserved _ephemeral tenant id is refused at the op boundary (same as
// _shared), so a persistent volume can never collide with the run-scoped
// _ephemeral subtree.
func TestVolumeDefTool_ReservedEphemeralTenantRefused(t *testing.T) {
	tool, base, _, cleanup := volumeDefFixture(t)
	defer cleanup()
	ctx := tools.WithVolumeDefPolicy(
		tools.WithRunIdentity(base, tools.RunIdentityValue{TenantID: "_ephemeral"}),
		tools.VolumeDefPolicyValue{Scopes: []string{"any"}})
	if _, res := vdExec(t, tool, ctx, `{"op":"create","name":"x"}`); !res.IsError || !strings.Contains(res.Text, "reserved") {
		t.Errorf("_ephemeral tenant must be refused; got %s", res.Text)
	}
}

// ---- PurgeEphemeralRunTree fence (RFC AH Phase 2b) ----

// the shared purge fence removes a real per-run ephemeral subtree.
func TestPurgeEphemeralRunTree_RemovesSubtree(t *testing.T) {
	root, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	runDir := filepath.Join(root, "_ephemeral", "run-1")
	if err := os.MkdirAll(filepath.Join(runDir, "work"), 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(runDir, "work", "f.txt"), []byte("x"), 0o600); err != nil {
		t.Fatal(err)
	}
	removed, err := PurgeEphemeralRunTree(root, "run-1", "test")
	if err != nil || !removed {
		t.Fatalf("purge: removed=%v err=%v", removed, err)
	}
	if _, err := os.Stat(runDir); !os.IsNotExist(err) {
		t.Errorf("run dir survived purge: %v", err)
	}
	// Idempotent: a second purge of the (now-gone) subtree is a no-op success.
	if removed, err := PurgeEphemeralRunTree(root, "run-1", "test"); err != nil || removed {
		t.Errorf("second purge: removed=%v err=%v, want (false, nil)", removed, err)
	}
}

// the fence refuses to follow a symlinked run dir that escapes the dynamic
// root — a swapped symlink can't redirect the RemoveAll outside the volume.
func TestPurgeEphemeralRunTree_RefusesSymlinkEscape(t *testing.T) {
	root, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	// A precious dir OUTSIDE the dynamic root.
	outside, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	precious := filepath.Join(outside, "precious.txt")
	if err := os.WriteFile(precious, []byte("keep"), 0o600); err != nil {
		t.Fatal(err)
	}
	// <root>/_ephemeral/run-evil -> outside  (a symlink that escapes).
	if err := os.MkdirAll(filepath.Join(root, "_ephemeral"), 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(outside, filepath.Join(root, "_ephemeral", "run-evil")); err != nil {
		t.Fatal(err)
	}

	removed, err := PurgeEphemeralRunTree(root, "run-evil", "test")
	if removed || err == nil {
		t.Fatalf("FENCE BREACH: symlink escape was followed (removed=%v err=%v)", removed, err)
	}
	if _, statErr := os.Stat(precious); statErr != nil {
		t.Errorf("FENCE BREACH: purge followed the symlink and deleted %q: %v", precious, statErr)
	}
}

// the fence refuses an empty run id (which would otherwise resolve to the
// _ephemeral parent dir itself — a far wider delete).
func TestPurgeEphemeralRunTree_RefusesEmptyRunID(t *testing.T) {
	root, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	if removed, err := PurgeEphemeralRunTree(root, "", "test"); removed || err == nil {
		t.Errorf("empty run id must be refused; removed=%v err=%v", removed, err)
	}
}

// ---- effectiveRoot ephemeral tier (RFC AH Phase 2b) ----

// a NAMED ephemeral volume resolves to its root via effectiveRoot; rw allows
// a write, ro refuses; a name not in the set falls through (and with no
// VolumePolicy → DENY, RFC AH Phase 3 sandbox-by-default; the legacy
// fallback root is gone).
func TestEffectiveRoot_EphemeralTier(t *testing.T) {
	set := tools.NewEphemeralVolumeSet()
	set.Add("work", tools.EphemeralVolumeRef{Root: "/pool/_ephemeral/run-1/work", ReadOnly: false})
	set.Add("ref", tools.EphemeralVolumeRef{Root: "/pool/_ephemeral/run-1/ref", ReadOnly: true})
	ctx := tools.WithEphemeralVolumes(context.Background(), set)

	// rw ephemeral resolves for both read + write.
	if got, err := effectiveRoot(ctx, "work", false); err != nil || got != "/pool/_ephemeral/run-1/work" {
		t.Errorf("read work = %q err=%v", got, err)
	}
	if got, err := effectiveRoot(ctx, "work", true); err != nil || got != "/pool/_ephemeral/run-1/work" {
		t.Errorf("write work = %q err=%v", got, err)
	}
	// ro ephemeral allows read, refuses write.
	if got, err := effectiveRoot(ctx, "ref", false); err != nil || got != "/pool/_ephemeral/run-1/ref" {
		t.Errorf("read ref = %q err=%v", got, err)
	}
	if _, err := effectiveRoot(ctx, "ref", true); err == nil || !strings.Contains(err.Error(), "read-only") {
		t.Errorf("write ref must refuse ro; err=%v", err)
	}
	// A name not in the set + no VolumePolicy → DENY (no fallback root).
	if _, err := effectiveRoot(ctx, "other", false); err == nil || !strings.Contains(err.Error(), "no filesystem volume available") {
		t.Errorf("unknown name + no policy must deny; err=%v", err)
	}
}

// containment is still enforced for an ephemeral volume: effectiveRoot only
// picks the root; resolveInsideRoot rejects a `..` escape under it.
func TestEffectiveRoot_EphemeralStillContains(t *testing.T) {
	parent, err := filepath.EvalSymlinks(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	realRoot := filepath.Join(parent, "vol")
	if err := os.MkdirAll(realRoot, 0o700); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(realRoot, "ok.txt"), []byte("x"), 0o600); err != nil {
		t.Fatal(err)
	}
	// A real file OUTSIDE the volume root (a sibling under the parent), so the
	// `..` escape targets an EXISTING file — the rejection is the containment
	// check, not an ENOENT.
	if err := os.WriteFile(filepath.Join(parent, "secret.txt"), []byte("s"), 0o600); err != nil {
		t.Fatal(err)
	}
	set := tools.NewEphemeralVolumeSet()
	set.Add("work", tools.EphemeralVolumeRef{Root: realRoot})
	ctx := tools.WithEphemeralVolumes(context.Background(), set)

	root, err := effectiveRoot(ctx, "work", false)
	if err != nil {
		t.Fatalf("effectiveRoot: %v", err)
	}
	// In-root path resolves.
	if _, err := resolveInsideRoot(root, "ok.txt"); err != nil {
		t.Errorf("in-root resolve failed: %v", err)
	}
	// `..` escape to an EXISTING sibling is rejected — the ephemeral tier
	// never bypasses containment (resolveInsideRoot still applies).
	if _, err := resolveInsideRoot(root, "../secret.txt"); err == nil {
		t.Errorf("escape under ephemeral volume was NOT rejected")
	}
}
