// Copyright 2021 The Hugo 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 paths

import (
	"path/filepath"
	"testing"

	qt "github.com/frankban/quicktest"
)

func TestGetRelativePath(t *testing.T) {
	tests := []struct {
		path   string
		base   string
		expect interface{}
	}{
		{filepath.FromSlash("/a/b"), filepath.FromSlash("/a"), filepath.FromSlash("b")},
		{filepath.FromSlash("/a/b/c/"), filepath.FromSlash("/a"), filepath.FromSlash("b/c/")},
		{filepath.FromSlash("/c"), filepath.FromSlash("/a/b"), filepath.FromSlash("../../c")},
		{filepath.FromSlash("/c"), "", false},
	}
	for i, this := range tests {
		// ultimately a fancy wrapper around filepath.Rel
		result, err := GetRelativePath(this.path, this.base)

		if b, ok := this.expect.(bool); ok && !b {
			if err == nil {
				t.Errorf("[%d] GetRelativePath didn't return an expected error", i)
			}
		} else {
			if err != nil {
				t.Errorf("[%d] GetRelativePath failed: %s", i, err)
				continue
			}
			if result != this.expect {
				t.Errorf("[%d] GetRelativePath got %v but expected %v", i, result, this.expect)
			}
		}

	}
}

func TestMakePathRelative(t *testing.T) {
	type test struct {
		inPath, path1, path2, output string
	}

	data := []test{
		{"/abc/bcd/ab.css", "/abc/bcd", "/bbc/bcd", "/ab.css"},
		{"/abc/bcd/ab.css", "/abcd/bcd", "/abc/bcd", "/ab.css"},
	}

	for i, d := range data {
		output, _ := makePathRelative(d.inPath, d.path1, d.path2)
		if d.output != output {
			t.Errorf("Test #%d failed. Expected %q got %q", i, d.output, output)
		}
	}
	_, error := makePathRelative("a/b/c.ss", "/a/c", "/d/c", "/e/f")

	if error == nil {
		t.Errorf("Test failed, expected error")
	}
}

func TestGetDottedRelativePath(t *testing.T) {
	// on Windows this will receive both kinds, both country and western ...
	for _, f := range []func(string) string{filepath.FromSlash, func(s string) string { return s }} {
		doTestGetDottedRelativePath(f, t)
	}
}

func doTestGetDottedRelativePath(urlFixer func(string) string, t *testing.T) {
	type test struct {
		input, expected string
	}
	data := []test{
		{"", "./"},
		{urlFixer("/"), "./"},
		{urlFixer("post"), "../"},
		{urlFixer("/post"), "../"},
		{urlFixer("post/"), "../"},
		{urlFixer("tags/foo.html"), "../"},
		{urlFixer("/tags/foo.html"), "../"},
		{urlFixer("/post/"), "../"},
		{urlFixer("////post/////"), "../"},
		{urlFixer("/foo/bar/index.html"), "../../"},
		{urlFixer("/foo/bar/foo/"), "../../../"},
		{urlFixer("/foo/bar/foo"), "../../../"},
		{urlFixer("foo/bar/foo/"), "../../../"},
		{urlFixer("foo/bar/foo/bar"), "../../../../"},
		{"404.html", "./"},
		{"404.xml", "./"},
		{"/404.html", "./"},
	}
	for i, d := range data {
		output := GetDottedRelativePath(d.input)
		if d.expected != output {
			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
		}
	}
}

func TestMakeTitle(t *testing.T) {
	type test struct {
		input, expected string
	}
	data := []test{
		{"Make-Title", "Make Title"},
		{"MakeTitle", "MakeTitle"},
		{"make_title", "make_title"},
	}
	for i, d := range data {
		output := MakeTitle(d.input)
		if d.expected != output {
			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
		}
	}
}

// Replace Extension is probably poorly named, but the intent of the
// function is to accept a path and return only the file name with a
// new extension. It's intentionally designed to strip out the path
// and only provide the name. We should probably rename the function to
// be more explicit at some point.
func TestReplaceExtension(t *testing.T) {
	type test struct {
		input, newext, expected string
	}
	data := []test{
		// These work according to the above definition
		{"/some/random/path/file.xml", "html", "file.html"},
		{"/banana.html", "xml", "banana.xml"},
		{"./banana.html", "xml", "banana.xml"},
		{"banana/pie/index.html", "xml", "index.xml"},
		{"../pies/fish/index.html", "xml", "index.xml"},
		// but these all fail
		{"filename-without-an-ext", "ext", "filename-without-an-ext.ext"},
		{"/filename-without-an-ext", "ext", "filename-without-an-ext.ext"},
		{"/directory/mydir/", "ext", ".ext"},
		{"mydir/", "ext", ".ext"},
	}

	for i, d := range data {
		output := ReplaceExtension(filepath.FromSlash(d.input), d.newext)
		if d.expected != output {
			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
		}
	}
}

func TestExtNoDelimiter(t *testing.T) {
	c := qt.New(t)
	c.Assert(ExtNoDelimiter(filepath.FromSlash("/my/data.json")), qt.Equals, "json")
}

func TestFilename(t *testing.T) {
	type test struct {
		input, expected string
	}
	data := []test{
		{"index.html", "index"},
		{"./index.html", "index"},
		{"/index.html", "index"},
		{"index", "index"},
		{"/tmp/index.html", "index"},
		{"./filename-no-ext", "filename-no-ext"},
		{"/filename-no-ext", "filename-no-ext"},
		{"filename-no-ext", "filename-no-ext"},
		{"directory/", ""}, // no filename case??
		{"directory/.hidden.ext", ".hidden"},
		{"./directory/../~/banana/gold.fish", "gold"},
		{"../directory/banana.man", "banana"},
		{"~/mydir/filename.ext", "filename"},
		{"./directory//tmp/filename.ext", "filename"},
	}

	for i, d := range data {
		output := Filename(filepath.FromSlash(d.input))
		if d.expected != output {
			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
		}
	}
}

func TestFileAndExt(t *testing.T) {
	type test struct {
		input, expectedFile, expectedExt string
	}
	data := []test{
		{"index.html", "index", ".html"},
		{"./index.html", "index", ".html"},
		{"/index.html", "index", ".html"},
		{"index", "index", ""},
		{"/tmp/index.html", "index", ".html"},
		{"./filename-no-ext", "filename-no-ext", ""},
		{"/filename-no-ext", "filename-no-ext", ""},
		{"filename-no-ext", "filename-no-ext", ""},
		{"directory/", "", ""}, // no filename case??
		{"directory/.hidden.ext", ".hidden", ".ext"},
		{"./directory/../~/banana/gold.fish", "gold", ".fish"},
		{"../directory/banana.man", "banana", ".man"},
		{"~/mydir/filename.ext", "filename", ".ext"},
		{"./directory//tmp/filename.ext", "filename", ".ext"},
	}

	for i, d := range data {
		file, ext := fileAndExt(filepath.FromSlash(d.input), fpb)
		if d.expectedFile != file {
			t.Errorf("Test %d failed. Expected filename %q got %q.", i, d.expectedFile, file)
		}
		if d.expectedExt != ext {
			t.Errorf("Test %d failed. Expected extension %q got %q.", i, d.expectedExt, ext)
		}
	}
}

func TestFindCWD(t *testing.T) {
	type test struct {
		expectedDir string
		expectedErr error
	}

	// cwd, _ := os.Getwd()
	data := []test{
		//{cwd, nil},
		// Commenting this out. It doesn't work properly.
		// There's a good reason why we don't use os.Getwd(), it doesn't actually work the way we want it to.
		// I really don't know a better way to test this function. - SPF 2014.11.04
	}
	for i, d := range data {
		dir, err := FindCWD()
		if d.expectedDir != dir {
			t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedDir, dir)
		}
		if d.expectedErr != err {
			t.Errorf("Test %d failed. Expected %q but got %q", i, d.expectedErr, err)
		}
	}
}