package tui

import (
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

// makeFiles creates files (and intermediate dirs) under base for each
// relative path in rels. Returns base for convenience.
func makeFiles(t *testing.T, base string, rels ...string) string {
	t.Helper()
	for _, rel := range rels {
		full := filepath.Join(base, rel)
		if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
			t.Fatalf("mkdir: %v", err)
		}
		if err := os.WriteFile(full, []byte("x"), 0o644); err != nil {
			t.Fatalf("write: %v", err)
		}
	}
	return base
}

func TestQuotePastedFilePaths(t *testing.T) {
	// normalisePathToken's heuristics target Unix-shaped paths
	// (leading "/", "~", or "file://" with a "/" path component) —
	// the same shapes drag-and-drop produces on macOS and Linux.
	// Windows TempDir paths look like C:\Users\... which never
	// match those prefixes, so the test fixtures wouldn't exercise
	// any real code path on that platform.
	if runtime.GOOS == "windows" {
		t.Skip("path-quoting heuristics are unix-shaped")
	}
	dir := t.TempDir()
	makeFiles(t, dir,
		"foo bar.png",
		"file.png",
		"a.png",
		"b.png",
		"x.png",
		"it's.png",
		"only.png",
	)

	// file:// URL form needs URL-encoded spaces.
	fileURL := "file://" + dir + "/" + url.PathEscape("foo bar.png")
	// PathEscape doesn't escape "/" but escapes the space — that's
	// what macOS Finder produces. Build the path-only segment so the
	// "file://" prefix is added cleanly.
	fileURLPath := dir + "/" + strings.ReplaceAll("foo bar.png", " ", "%20")
	fileURLForm := "file://" + fileURLPath
	_ = fileURL // keep linter happy if unused

	cases := []struct {
		name string
		in   string
		want string
	}{
		// macOS Terminal default: backslash-escaped spaces in path.
		{"backslash-escaped space", dir + `/foo\ bar.png`, `'` + dir + `/foo bar.png'`},

		// Plain absolute path with no special chars.
		{"plain absolute", dir + `/file.png`, `'` + dir + `/file.png'`},

		// file:// URL form; URL-decoded and scheme stripped.
		{"file url", fileURLForm, `'` + dir + `/foo bar.png'`},

		// Multi-file drop, each path quoted independently.
		{"two paths", dir + `/a.png ` + dir + `/b.png`, `'` + dir + `/a.png' '` + dir + `/b.png'`},

		// Already single-quoted: re-normalise to consistent quoting.
		{"already single quoted", `'` + dir + `/x.png'`, `'` + dir + `/x.png'`},

		// Already double-quoted: re-normalise.
		{"already double quoted", `"` + dir + `/x.png"`, `'` + dir + `/x.png'`},

		// Embedded apostrophe gets the standard '\'' splice escape.
		{"embedded apostrophe", dir + `/it's.png`, `'` + dir + `/it'\''s.png'`},

		// Plain prose left alone.
		{"prose", `hello world`, `hello world`},

		// Multi-line paste left alone (typical code paste).
		{"multiline", "foo\nbar", "foo\nbar"},

		// Anything containing shell metacharacters is left alone so a
		// crafted "drop" can't smuggle a command.
		{"metachar", `/foo;rm -rf /`, `/foo;rm -rf /`},

		// Mixed: valid path token quoted, surrounding words preserved.
		{"path mixed with prose", dir + `/only.png is good`, `'` + dir + `/only.png' is good`},

		// URL path segment that happens to start with "/" but doesn't
		// exist on disk: must NOT be quoted or chip-collapsed. This
		// is the regression that motivated the existence check.
		{"url path segment", `/de/downloads/dokumentenarchiv`, `/de/downloads/dokumentenarchiv`},

		// Non-existent path that looks like a real one: also untouched.
		{"non-existent absolute", `/this/does/not/exist.png`, `/this/does/not/exist.png`},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := quotePastedFilePaths(c.in)
			if got != c.want {
				t.Errorf("input %q\n  want %q\n  got  %q", c.in, c.want, got)
			}
		})
	}
}

// TestTildePathExists checks that ~ expansion in pathExists works
// when the home directory contains the candidate. We don't write
// into the user's real home; instead, override HOME for the test.
func TestTildePathExists(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip("path-quoting heuristics are unix-shaped")
	}
	tmp := t.TempDir()
	makeFiles(t, tmp, "tilde-test.png")
	t.Setenv("HOME", tmp)

	in := `~/tilde-test.png`
	want := `'~/tilde-test.png'`
	got := quotePastedFilePaths(in)
	if got != want {
		t.Errorf("input %q\n  want %q\n  got  %q", in, want, got)
	}

	// A tilde path that doesn't exist must be left alone.
	in = `~/no-such-file.png`
	got = quotePastedFilePaths(in)
	if got != in {
		t.Errorf("non-existent tilde path mutated: got %q want %q", got, in)
	}
}
