package pathutil

import (
	"errors"
	"fmt"
	"testing"
)

func TestCanonicalizeInvalidPathSentinel(t *testing.T) {
	tests := []struct {
		in      string
		wantMsg string
	}{
		{`F:\kimi_workspace\报告\090_prep`, "path contains backslash"},
		{"/data/../file.txt", `path contains ".." segment`},
		{"/data/./file.txt", `path contains "." segment`},
		{"/data/file\x00.txt", "path contains NUL character"},
		{"/data/file\x01.txt", "path contains control character 0x01"},
		{"/data/file\xed\xa0\x80.txt", "path contains invalid UTF-8"},
	}
	for _, tt := range tests {
		_, err := Canonicalize(tt.in)
		if err == nil {
			t.Errorf("Canonicalize(%q) expected error", tt.in)
			continue
		}
		if !errors.Is(err, ErrInvalidPath) {
			t.Errorf("Canonicalize(%q) error %v: errors.Is(ErrInvalidPath) = false", tt.in, err)
		}
		if err.Error() != tt.wantMsg {
			t.Errorf("Canonicalize(%q) error = %q, want %q", tt.in, err.Error(), tt.wantMsg)
		}
		// Wrapping must preserve the sentinel match (backend layers wrap with %w).
		if wrapped := fmt.Errorf("stat %s: %w", tt.in, err); !errors.Is(wrapped, ErrInvalidPath) {
			t.Errorf("Canonicalize(%q) wrapped error lost ErrInvalidPath match", tt.in)
		}
	}

	if _, err := CanonicalizeDir(`F:\dir`); !errors.Is(err, ErrInvalidPath) {
		t.Errorf("CanonicalizeDir with backslash: errors.Is(ErrInvalidPath) = false, err=%v", err)
	}
}

func TestCanonicalize(t *testing.T) {
	tests := []struct {
		in   string
		want string
		err  bool
	}{
		{"", "/", false},
		{"/", "/", false},
		{"/data/file.txt", "/data/file.txt", false},
		{"data/file.txt", "/data/file.txt", false},
		{"/data//file.txt", "/data/file.txt", false},
		{"/data/../file.txt", "", true},
		{"/data/./file.txt", "", true},
		{"/data/file\x00.txt", "", true},
		{"/data/file\x01.txt", "", true},
		{"/data/file\nname.txt", "/data/file\nname.txt", false},
		{"/data/file\r\nname.txt", "/data/file\r\nname.txt", false},
		{"/data/file\rname.txt", "/data/file\rname.txt", false},
		{"/data/file\tname.txt", "/data/file\tname.txt", false},
		{"/data/ leading.txt", "/data/ leading.txt", false},
		{"/data/trailing.txt ", "/data/trailing.txt ", false},
		{"  ", "/  ", false},
		{"/data/file\x02name.txt", "", true},
		{"/data/file\x1fname.txt", "", true},
		{"/data/file\x7fname.txt", "/data/file\x7fname.txt", false},
		{"/data\\file.txt", "", true},
		{"/data/file.txt/", "/data/file.txt", false},
	}
	for _, tt := range tests {
		got, err := Canonicalize(tt.in)
		if tt.err && err == nil {
			t.Errorf("Canonicalize(%q) expected error", tt.in)
		}
		if !tt.err && err != nil {
			t.Errorf("Canonicalize(%q) unexpected error: %v", tt.in, err)
		}
		if got != tt.want {
			t.Errorf("Canonicalize(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}

func TestCanonicalizeDir(t *testing.T) {
	tests := []struct {
		in   string
		want string
	}{
		{"/", "/"},
		{"/data", "/data/"},
		{"/data/", "/data/"},
		{"/data/sub", "/data/sub/"},
	}
	for _, tt := range tests {
		got, err := CanonicalizeDir(tt.in)
		if err != nil {
			t.Errorf("CanonicalizeDir(%q) error: %v", tt.in, err)
		}
		if got != tt.want {
			t.Errorf("CanonicalizeDir(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}

func TestParentPath(t *testing.T) {
	tests := []struct {
		in   string
		want string
	}{
		{"/", "/"},
		{"/data/file.txt", "/data/"},
		{"/data/", "/"},
		{"/a/b/c/", "/a/b/"},
		{"/a", "/"},
	}
	for _, tt := range tests {
		got := ParentPath(tt.in)
		if got != tt.want {
			t.Errorf("ParentPath(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}

func TestBaseName(t *testing.T) {
	tests := []struct {
		in   string
		want string
	}{
		{"/data/file.txt", "file.txt"},
		{"/data/", "data"},
		{"/", "/"},
	}
	for _, tt := range tests {
		got := BaseName(tt.in)
		if got != tt.want {
			t.Errorf("BaseName(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}

func TestIsDir(t *testing.T) {
	tests := []struct {
		in   string
		want bool
	}{
		{"/", true},
		{"/data/", true},
		{"/data/file.txt", false},
	}
	for _, tt := range tests {
		got := IsDir(tt.in)
		if got != tt.want {
			t.Errorf("IsDir(%q) = %v, want %v", tt.in, got, tt.want)
		}
	}
}

func TestExt(t *testing.T) {
	tests := []struct {
		in   string
		want string
	}{
		{"/data/file.txt", ".txt"},
		{"/data/file", ""},
		{"/data/file.tar.gz", ".gz"},
	}
	for _, tt := range tests {
		got := Ext(tt.in)
		if got != tt.want {
			t.Errorf("Ext(%q) = %q, want %q", tt.in, got, tt.want)
		}
	}
}
