// Copyright © 2014 Steve Francia <spf@spf13.com>.
// Copyright 2009 The Go Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package afero

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

// contains returns true if vector contains the string s.
func contains(vector []string, s string) bool {
	for _, elem := range vector {
		if elem == s {
			return true
		}
	}
	return false
}

func setupGlobDirRoot(t *testing.T, fs Fs) string {
	path := testDir(fs)
	setupGlobFiles(t, fs, path)
	return path
}

func setupGlobDirReusePath(t *testing.T, fs Fs, path string) string {
	testRegistry[fs] = append(testRegistry[fs], path)
	return setupGlobFiles(t, fs, path)
}

func setupGlobFiles(t *testing.T, fs Fs, path string) string {
	testSubDir := filepath.Join(path, "globs", "bobs")
	err := fs.MkdirAll(testSubDir, 0o700)
	if err != nil && !os.IsExist(err) {
		t.Fatal(err)
	}

	f, err := fs.Create(filepath.Join(testSubDir, "/matcher"))
	if err != nil {
		t.Fatal(err)
	}
	f.WriteString("Testfile 1 content")
	f.Close()

	f, err = fs.Create(filepath.Join(testSubDir, "/../submatcher"))
	if err != nil {
		t.Fatal(err)
	}
	f.WriteString("Testfile 2 content")
	f.Close()

	f, err = fs.Create(filepath.Join(testSubDir, "/../../match"))
	if err != nil {
		t.Fatal(err)
	}
	f.WriteString("Testfile 3 content")
	f.Close()

	return testSubDir
}

func TestGlob(t *testing.T) {
	defer removeAllTestFiles(t)
	var testDir string
	for i, fs := range Fss {
		if i == 0 {
			testDir = setupGlobDirRoot(t, fs)
		} else {
			setupGlobDirReusePath(t, fs, testDir)
		}
	}

	globTests := []struct {
		pattern, result string
	}{
		{testDir + "/globs/bobs/matcher", testDir + "/globs/bobs/matcher"},
		{testDir + "/globs/*/mat?her", testDir + "/globs/bobs/matcher"},
		{testDir + "/globs/bobs/../*", testDir + "/globs/submatcher"},
		{testDir + "/match", testDir + "/match"},
	}

	for _, fs := range Fss {

		for _, tt := range globTests {
			pattern := tt.pattern
			result := tt.result
			if runtime.GOOS == "windows" {
				pattern = filepath.Clean(pattern)
				result = filepath.Clean(result)
			}
			matches, err := Glob(fs, pattern)
			if err != nil {
				t.Errorf("Glob error for %q: %s", pattern, err)
				continue
			}
			if !contains(matches, result) {
				t.Errorf("Glob(%#q) = %#v want %v", pattern, matches, result)
			}
		}
		for _, pattern := range []string{"no_match", "../*/no_match"} {
			matches, err := Glob(fs, pattern)
			if err != nil {
				t.Errorf("Glob error for %q: %s", pattern, err)
				continue
			}
			if len(matches) != 0 {
				t.Errorf("Glob(%#q) = %#v want []", pattern, matches)
			}
		}

	}
}

func TestGlobSymlink(t *testing.T) {
	defer removeAllTestFiles(t)

	fs := &OsFs{}
	testDir := setupGlobDirRoot(t, fs)

	err := os.Symlink("target", filepath.Join(testDir, "symlink"))
	if err != nil {
		t.Skipf("skipping on %s", runtime.GOOS)
	}

	globSymlinkTests := []struct {
		path, dest string
		brokenLink bool
	}{
		{"test1", "link1", false},
		{"test2", "link2", true},
	}

	for _, tt := range globSymlinkTests {
		path := filepath.Join(testDir, tt.path)
		dest := filepath.Join(testDir, tt.dest)
		f, err := fs.Create(path)
		if err != nil {
			t.Fatal(err)
		}
		if err := f.Close(); err != nil {
			t.Fatal(err)
		}
		err = os.Symlink(path, dest)
		if err != nil {
			t.Fatal(err)
		}
		if tt.brokenLink {
			// Break the symlink.
			fs.Remove(path)
		}
		matches, err := Glob(fs, dest)
		if err != nil {
			t.Errorf("GlobSymlink error for %q: %s", dest, err)
		}
		if !contains(matches, dest) {
			t.Errorf("Glob(%#q) = %#v want %v", dest, matches, dest)
		}
	}
}

func TestGlobError(t *testing.T) {
	for _, fs := range Fss {
		_, err := Glob(fs, "[7]")
		if err != nil {
			t.Error("expected error for bad pattern; got none")
		}
	}
}
