package pkg

import (
	"fmt"
	"io"
	"path/filepath"
	"strings"
	"testing"

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

const (
	DUMMY_PKG1                  = "pkg1"
	DUMMY_PKG2                  = "pkg2"
	DUMMY_PKG3                  = "pkg3"
	DUMMY_PKG3_DOWNLOAD         = "foo.tar.gz"
	DUMMY_VERSION               = "1.0"
	DUMMY_BLACKLISTED_PKG       = "blacklisted_pkg"
	DUMMY_GREYLISTED_PKG        = "greylisted_pkg"
	DUMMY_OTHER_BLACKLISTED_PKG = "other_blacklisted_pkg"
	DUMMY_PKG_FILE_CONTENT      = DUMMY_PKG1 + "\n" + DUMMY_PKG2 + "\n"
	MOCK_PULL                   = "echo mocking pull"
	MOCK_CLEAN                  = "echo mocking clean"
	MOCK_SYNC                   = "echo mocking sync"
	MOCK_DOWNLOAD               = "echo mocking download"
	MOCK_BUILD                  = "echo mocking build"
	MOCK_INSTALL                = "echo mocking install"
	MOCK_PKG_SLACKBUILD_CONTENT = "exit 1"
	MOCK_PKG_README_CONTENT     = "README file\n"
	MOCK_PKG_SLACK_DES_CONTENT  = `# HOW TO EDIT THIS FILE:
# The \"handy ruler\" below makes it easier to edit a package description.  Line
# up the first '|' above the ':' following the base package name, and the '|'
# on the right side marks the last column you can put a character in.  You must
# make exactly 11 lines for the formatting to be correct.  It's also
# customary to leave one space after the ':'.

      |-----handy-ruler------------------------------------------------------|
name: name Description
name:
name:
name:
name:
name:
name:
name:
name:
name:
name:
`
)

var dummyPkg3 = sborepo.Package{Name: DUMMY_PKG3}

func makeDummyRepo(t testing.TB) (string, string) {
	t.Helper()
	repo := t.TempDir()
	tmpDir := t.TempDir()

	pkgCreator := sborepo.PackageCreator{
		Name:              DUMMY_PKG1,
		Path:              filepath.Join(repo, "category1"),
		Version:           DUMMY_VERSION,
		Requires:          DUMMY_PKG2,
		SlackBuildContent: MOCK_PKG_SLACKBUILD_CONTENT,
		READMEContent:     MOCK_PKG_README_CONTENT,
		SlackDescContent:  MOCK_PKG_SLACK_DES_CONTENT,
	}
	pkgCreator.Create()

	pkgCreator.Path = filepath.Join(repo, "category2")
	pkgCreator.Requires = ""
	for _, pkg := range []string{DUMMY_PKG2, DUMMY_BLACKLISTED_PKG,
		DUMMY_GREYLISTED_PKG, DUMMY_OTHER_BLACKLISTED_PKG} {
		pkgCreator.Name = pkg
		pkgCreator.Create()
	}

	pkgCreator.Name = DUMMY_PKG3
	pkgCreator.Download = DUMMY_PKG3_DOWNLOAD
	pkgCreator.MD5Sum = "d41d8cd98f00b204e9800998ecf8427e" // empty file

	dummyPkg3 = pkgCreator.Create()

	must.Content2File(dummyPkg3.SlackBuild(), "#!/bin/sh\necho building "+DUMMY_PKG3)
	must.Content2File(filepath.Join(tmpDir, fmt.Sprintf("%s-%s-%s-1_SBo.txz", DUMMY_PKG3, DUMMY_VERSION, "noarch")), "")
	return repo, tmpDir
}

func DummyArgs(t testing.TB) RunArgs {
	t.Helper()

	var (
		repoPath, tmpDir = makeDummyRepo(t)
		configDir        = t.TempDir()
		greylist         = filepath.Join(configDir, "greylist")
		blacklist        = filepath.Join(configDir, "blacklist")
		otherBlacklist   = filepath.Join(configDir, "other_blacklist")
		pkgFile          = filepath.Join(configDir, "pkgfile")
		preBuildScript   = filepath.Join(configDir, DUMMY_PKG2+PREBUILD_EXT)
		downloadScript   = fmt.Sprintf(`#!/bin/sh`+"\n"+MOCK_DOWNLOAD+
			`;for f in "$@"; do touch "%s/${f##*/}"; done`, tmpDir)
		downloadCmd = filepath.Join(t.TempDir(), "mock_download.sh")
	)

	must.Content2File(pkgFile, DUMMY_PKG_FILE_CONTENT)
	must.Content2File(greylist, DUMMY_GREYLISTED_PKG)
	must.Content2File(blacklist, DUMMY_BLACKLISTED_PKG)
	must.Content2File(otherBlacklist, DUMMY_OTHER_BLACKLISTED_PKG)

	must.Content2File(preBuildScript, "#!/bin/sh\necho prebuild script")
	must.Chmod(preBuildScript, 0o755)

	must.Content2File(downloadCmd, downloadScript)
	must.Chmod(downloadCmd, 0o755)

	return RunArgs{
		Streams: Streams{
			Out: io.Discard,
			In:  strings.NewReader(""),
			Err: io.Discard,
		},
		Argv: []string{"pkg"},
		Config: Config{
			RepoPath:             repoPath,
			DocDir:               t.TempDir(),
			TmpDir:               tmpDir,
			ConfigDir:            configDir,
			Greylist:             greylist,
			Blacklist:            blacklist,
			OtherBlacklist:       otherBlacklist,
			DefaultPkgFile:       pkgFile,
			SlackwareVersionFile: "",
			PullCmd:              MOCK_PULL,
			SyncCmd:              MOCK_SYNC,
			CleanCmd:             MOCK_CLEAN,
			DownloadCmd:          downloadCmd,
			InstallCmd:           MOCK_INSTALL,
			FallbackPager:        "echo mocking pager: ",
		},
	}
}

func createPkgTestfiles(p *sborepo.Package, t *testing.T) {
	pkg := sborepo.PackageCreator{
		Name:              p.Name,
		Path:              t.TempDir(),
		Version:           DUMMY_VERSION,
		Download:          DUMMY_PKG3_DOWNLOAD,
		MD5Sum:            "d41d8cd98f00b204e9800998ecf8427e",
		SlackBuildContent: MOCK_PKG_SLACKBUILD_CONTENT,
		READMEContent:     MOCK_PKG_README_CONTENT,
		SlackDescContent:  MOCK_PKG_SLACK_DES_CONTENT,
	}.Create()
	*p = pkg
}

func mustBeEqual(t *testing.T, got, expected interface{}) {
	t.Helper()
	if got != expected {
		t.Fatalf("Expected '%v', got '%v'", expected, got)
	}
}

func mustBeEqualStringSlices(t *testing.T, got, expected []string) {
	t.Helper()
	if len(got) != len(expected) {
		t.Fatalf("Expected %v and %v to have the same length, got %d and %d",
			expected, got, len(expected), len(got))
	}
	for i := range got {
		mustBeEqual(t, got[i], expected[i])
	}
}

func mustContain(t *testing.T, output string, expected ...string) {
	t.Helper()
	for _, s := range expected {
		if !strings.Contains(output, s) {
			t.Errorf("Expected %v to be in %v", s, output)
		}
	}
}

func mustNotContain(t *testing.T, output string, expected ...string) {
	t.Helper()
	for _, s := range expected {
		if strings.Contains(output, s) {
			t.Errorf("Expected %v not to be in %v", s, output)
		}
	}
}
