package runtime

import (
	"context"
	"os"
	"path/filepath"
	"testing"

	"github.com/flothus/tmux-xterm-research/server-go/internal/harness/event"
	"github.com/flothus/tmux-xterm-research/server-go/internal/harness/store"
	"github.com/flothus/tmux-xterm-research/server-go/internal/harness/transport"
)

// TestWriteFileRejectsAbsolutePathsAndSymlinkEscape is the V35 regression:
// the logical-glob zone check accepts paths like "client/x" matching glob
// "client/**/*"; but if "client" is a symlink, the actual write escapes the
// project root. Absolute paths should also be rejected outright.
func TestWriteFileRejectsAbsolutePathsAndSymlinkEscape(t *testing.T) {
	tmp := t.TempDir()
	st, err := store.Open(filepath.Join(tmp, "harness.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer st.Close()
	bus := event.NewBus(st)
	q := transport.New(st, bus)

	// Set up: ProjectRoot/client is a symlink to ProjectRoot's parent.
	root := filepath.Join(tmp, "project")
	outside := filepath.Join(tmp, "outside")
	if err := os.MkdirAll(root, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.MkdirAll(outside, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.Symlink(outside, filepath.Join(root, "client")); err != nil {
		t.Fatal(err)
	}

	a := NewAgent(nil, st, bus, q, "agent-1", "worker", "run-1",
		[]string{"client/**/*"}, []string{"write_file"}, "scripted", 0)
	a.ProjectRoot = root

	// 1. Absolute path: must be rejected even if it happens to be under
	// the project root.
	err = a.writeArtifactFile(context.Background(), "", filepath.Join(root, "x.txt"), "hi")
	if err == nil {
		t.Errorf("absolute path was accepted; want rejection")
	}

	// 2. Symlink escape: write to client/escape.txt, which is actually
	// outside/escape.txt because client is a symlink.
	err = a.writeArtifactFile(context.Background(), "", filepath.Join("client", "escape.txt"), "evil")
	if err == nil {
		t.Errorf("symlink escape was accepted; want rejection")
	}
	if _, statErr := os.Stat(filepath.Join(outside, "escape.txt")); statErr == nil {
		t.Errorf("escape.txt was created outside the project root — symlink escape succeeded")
	}

	// 3. Normal in-zone write under a real (non-symlink) dir should work.
	realDir := filepath.Join(root, "server")
	if err := os.MkdirAll(realDir, 0o755); err != nil {
		t.Fatal(err)
	}
	// Make zone permit it.
	a.ZoneScope = []string{"server/**/*"}
	if err := a.writeArtifactFile(context.Background(), "", filepath.Join("server", "ok.txt"), "ok"); err != nil {
		t.Errorf("legitimate in-zone write rejected: %v", err)
	}
}
