package sborepo

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

	"gitlab.com/M0M097/pkg/internal/must"
)

func TestDependencies(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	if len(pkg.Dependencies()) != 2 {
		t.Error("Dependencies() returned an incorrect value")
	}
}

func TestIsReadmeRequired(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	if !pkg.IsReadmeRequired() {
		t.Error("IsReadmeRequired() returned an incorrect value")
	}
}

func TestAllReadmes(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	// Create a README.Slackware file
	must.Create(filepath.Join(pkg.Path(), "README.Slackware"))

	if pkg.AllReadmes() != pkg.Readme()+" "+pkg.Path()+"/README.Slackware" {
		t.Error("AllReadmes() returned an incorrect value")
	}
}

func TestDownloadURLs(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	if len(pkg.DownloadURLs()) != 1 ||
		pkg.DownloadURLs()[0] != "http://example.com/foo.tar.gz" {
		t.Error("DownloadURLs() returned an incorrect value")
	}
}

func TestMD5Sums(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t) // Only MD5SUM (no MD5SUM_x86_64)
	mustBeEqual(t, len(pkg.MD5Sums()), 1)
	mustBeEqual(t, pkg.MD5Sums()[0], DUMMY_MD5SUM)

	createTestfileArgs(&pkg, t, "", "", "", "8664", "") // MD5SUM_x86_64
	mustBeEqual(t, len(pkg.MD5Sums()), 1)
	mustBeEqual(t, pkg.MD5Sums()[0], "8664")

	createTestfileArgs(&pkg, t, "", "1234567890", "", "8664", "") // both
	mustBeEqual(t, len(pkg.MD5Sums()), 1)
	mustBeEqual(t, pkg.MD5Sums()[0], "8664")
}

func TestDownloadNames(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t) // Only DOWNLOAD (no DOWNLOAD_x86_64)
	if len(pkg.DownloadNames()) != 1 || pkg.DownloadNames()[0] != "foo.tar.gz" {
		t.Error("Incorrect value, with only DOWNLOAD")
	}
	createTestfileArgs(&pkg, t, "", "", "foo_x86_64", "", "") // DOWNLOAD_x86_64
	if len(pkg.DownloadNames()) != 1 || pkg.DownloadNames()[0] != "foo_x86_64" {
		t.Error("Incorrect value, with only DOWNLOAD_x86_64")
	}
	createTestfileArgs(&pkg, t, "foo.tar.gz", "", "foo_x86_64", "", "") // both
	if len(pkg.DownloadNames()) != 1 || pkg.DownloadNames()[0] != "foo_x86_64" {
		t.Error("Incorrect value, when both DOWNLOAD_x86_64 and DOWNLOAD are present")
	}
	createTestfileArgs(&pkg, t, "foo.tar.gz \\\n\tbar.tar.gz\\\n    baz.tar.gz", "", "", "", "") // multiple
	if len(pkg.DownloadNames()) != 3 ||
		pkg.DownloadNames()[0] != "foo.tar.gz" ||
		pkg.DownloadNames()[1] != "bar.tar.gz" ||
		pkg.DownloadNames()[2] != "baz.tar.gz" {
		t.Error("Incorrect value, with multiple DOWNLOAD")
	}
}

func TestIsUpToDate(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	docDir := t.TempDir()
	if pkg.IsUpToDate(docDir, "random_kernel_version") {
		t.Error("Returns true although the package is not installed")
	}
	must.Mkdir(docDir + "/name-1.0")
	must.Content2File(docDir+"/name-1.0/name.SlackBuild", "differs")
	if pkg.IsUpToDate(docDir, "random_kernel_version") {
		t.Error("Returns true although the package is not up-to-date")
	}
	must.Content2File(docDir+"/name-1.0/name.SlackBuild", MOCK_PKG_SLACKBUILD_CONTENT)
	if !pkg.IsUpToDate(docDir, "random_kernel_version") {
		t.Error("Returns false although the package is up-to-date")
	}
}

func TestIsUpToDateKernelVersion(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	docDir := t.TempDir()
	must.Mkdir(docDir + "/name-1.0_random_kernel_version")
	must.Content2File(docDir+"/name-1.0_random_kernel_version/name.SlackBuild", MOCK_PKG_SLACKBUILD_CONTENT)
	if !pkg.IsUpToDate(docDir, "random_kernel_version") {
		t.Error("Returns false although the package is up-to-date")
	}
}

func TestQueryInfoFileMissingKey(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)

	if got := pkg.queryInfoFile("non-existing-key"); got != "" {
		t.Fatalf("expected an empty string for missing key, got %q", got)
	}
}

func TestPackage_FindBuildPkg(t *testing.T) {
	t.Run("Success_MatchingFile", func(t *testing.T) {
		// 1) Create a package with a known FullName() "name-1.0"
		pkg := MockPkg()
		createPkgTestfiles(&pkg, t)

		// 2) Create a temp directory
		tmpDir := t.TempDir()

		// 3) Make a file in tmpDir that matches the regex: pkg.FullName()+".*_SBo.*"
		//    e.g. "foo-1.0-x86_64-1_SBo.tgz"
		fileName := pkg.FullName() + "-x86_64-1_SBo.tgz" // "foo-1.0-x86_64-1_SBo.tgz"
		fullPath := filepath.Join(tmpDir, fileName)
		if err := os.WriteFile(fullPath, []byte("dummy content"), 0o644); err != nil {
			t.Fatalf("failed to create slackpkg: %v", err)
		}

		// 4) Call InstallCmd and ensure it returns "installpkg /tempDir/foo-1.0-x86_64-1_SBo.tgz"
		got := pkg.FindBuildPkg(tmpDir)
		want := fullPath
		if got != want {
			t.Errorf("expected %q, got %q", want, got)
		}
	})

	t.Run("Panic_NoMatchingFile", func(t *testing.T) {
		// 1) Package with some full name
		pkg := MockPkg()
		createPkgTestfiles(&pkg, t)

		// 2) Temp directory has a file that DOES NOT match the package's regex
		tmpDir := t.TempDir()
		if err := os.WriteFile(filepath.Join(tmpDir, "randomfile_SBo.tgz"), []byte(""), 0o644); err != nil {
			t.Fatalf("failed to create a dummy file: %v", err)
		}

		// 3) Expect panic because no file matches "bar-2.5.*_SBo.*"
		defer func() {
			if r := recover(); r == nil {
				t.Error("expected a panic but did not get one")
			} else {
				// Optionally check the panic message
				msg, ok := r.(string)
				if ok && !strings.Contains(msg, "No package to install") {
					t.Errorf("expected panic message to contain 'No package to install', got %q", msg)
				}
			}
		}()

		_ = pkg.FindBuildPkg(tmpDir) // should panic
		t.Error("InstallCmd should have panicked before this line")
	})
}

func TestDescription(t *testing.T) {
	pkg := MockPkg()
	createPkgTestfiles(&pkg, t)
	if pkg.Description() != pkg.Name+" Description" {
		t.Error("Description() returned an incorrect value")
	}
	// Now we check an ill-formatted slack-desc file
	pkg = PackageCreator{
		Name:             pkg.Name,
		Path:             filepath.Join(t.TempDir(), "category"),
		SlackDescContent: "foobarbaz", // slackDescContent
	}.Create()
	mustBeEqual(t, pkg.Description(), pkg.Name)
}

func TestWrite(t *testing.T) {
	pkg := MockPkg()
	var output strings.Builder
	pkg.Write(&output)
	if output.String() != pkg.Name+"\n" {
		t.Error("Write() did not write the package name correctly")
	}
}

func TestPackageNameFromFullName(t *testing.T) {
	expected := "pkg"

	pkgName := PackageNameFromFullName(expected + "-1.0")
	mustBeEqual(t, pkgName, expected)

	pkgName = PackageNameFromFullName(expected + "-1.0_kernel5.10.0_1.0.0_amd64")
	mustBeEqual(t, pkgName, expected)

	pkgName = PackageNameFromFullName("pkg-foo-1.0")
	mustBeEqual(t, pkgName, "pkg-foo")

	pkgName = PackageNameFromFullName("pkg")
	mustBeEqual(t, pkgName, "")
}
