package sourcearchive

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

	"github.com/goreleaser/goreleaser/v2/internal/artifact"
	"github.com/goreleaser/goreleaser/v2/internal/testctx"
	"github.com/goreleaser/goreleaser/v2/internal/testlib"
	"github.com/goreleaser/goreleaser/v2/pkg/config"
	"github.com/goreleaser/goreleaser/v2/pkg/context"
	"github.com/stretchr/testify/require"
)

func TestArchive(t *testing.T) {
	for _, format := range []string{"tar.gz", "tar", "zip"} {
		t.Run(format, func(t *testing.T) {
			tmp := testlib.Mktmp(t)
			require.NoError(t, os.Mkdir("dist", 0o744))

			testlib.GitInit(t)
			require.NoError(t, os.WriteFile("code.rb", []byte("not really code"), 0o655))
			require.NoError(t, os.WriteFile("code.py", []byte("print 1"), 0o655))
			require.NoError(t, os.WriteFile("README.md", []byte("# my dope fake project"), 0o655))
			require.NoError(t, os.WriteFile("ملف.go", []byte("محتوى عربي"), 0o655))
			require.NoError(t, os.WriteFile("🤔.patch", []byte("thinking"), 0o655))
			require.NoError(t, os.WriteFile(".gitignore", []byte(`
added-later.txt
ignored.txt
code.txt
subfolder/
			`), 0o655))

			require.NoError(t, os.WriteFile(".gitattributes", []byte(`
.VERSION export-subst
			`), 0o655))
			require.NoError(t, os.WriteFile(".VERSION", []byte("$Format:%d$"), 0o655))
			testlib.GitAdd(t)
			testlib.GitCommit(t, "feat: first")
			testlib.GitTag(t, "v1.0.0")
			require.NoError(t, os.WriteFile("added-later.txt", []byte("this file was added later"), 0o655))
			require.NoError(t, os.WriteFile("ignored.md", []byte("never added"), 0o655))
			require.NoError(t, os.WriteFile("code.txt", []byte("not really code"), 0o655))
			require.NoError(t, os.WriteFile("ملف.txt", []byte("محتوى عربي"), 0o655))
			require.NoError(t, os.WriteFile("🤝", []byte("it works"), 0o655))
			require.NoError(t, os.MkdirAll("subfolder", 0o755))
			require.NoError(t, os.WriteFile("subfolder/file.md", []byte("a file within a folder, added later"), 0o655))

			t.Run("with extra files", func(t *testing.T) {
				doVerifyTestArchive(
					t,
					testctx.WrapWithCfg(t.Context(),
						config.Project{
							ProjectName: "foo",
							Dist:        "dist",
							Source: config.Source{
								Format:         format,
								Enabled:        true,
								PrefixTemplate: "{{ .ProjectName }}-{{ .Version }}/",
								Files: []config.File{
									{Source: "*.txt"},
									{Source: "subfolder/*"},
								},
							},
						},
						testctx.WithCommit("HEAD"),
						testctx.WithVersion("1.0.0"),
						testctx.WithCurrentTag("v1.0.0")),

					tmp,
					format,
					[]string{
						"foo-1.0.0/",
						"foo-1.0.0/.VERSION",
						"foo-1.0.0/.gitattributes",
						"foo-1.0.0/.gitignore",
						"foo-1.0.0/README.md",
						"foo-1.0.0/added-later.txt",
						"foo-1.0.0/code.py",
						"foo-1.0.0/code.rb",
						"foo-1.0.0/code.txt",
						"foo-1.0.0/subfolder/file.md",
						"foo-1.0.0/ملف.go",
						"foo-1.0.0/ملف.txt",
						"foo-1.0.0/🤔.patch",
					},
				)
			})

			t.Run("simple", func(t *testing.T) {
				doVerifyTestArchive(
					t,
					testctx.WrapWithCfg(t.Context(),
						config.Project{
							ProjectName: "foo",
							Dist:        "dist",
							Source: config.Source{
								Format:         format,
								Enabled:        true,
								PrefixTemplate: "{{ .ProjectName }}-{{ .Version }}/",
							},
						},
						testctx.WithCommit("HEAD"),
						testctx.WithVersion("1.0.0"),
						testctx.WithCurrentTag("v1.0.0")),

					tmp,
					format,
					[]string{
						"foo-1.0.0/",
						"foo-1.0.0/.VERSION",
						"foo-1.0.0/.gitattributes",
						"foo-1.0.0/.gitignore",
						"foo-1.0.0/README.md",
						"foo-1.0.0/code.py",
						"foo-1.0.0/code.rb",
						"foo-1.0.0/ملف.go",
						"foo-1.0.0/🤔.patch",
					},
				)
			})
		})
	}
}

func doVerifyTestArchive(tb testing.TB, ctx *context.Context, tmp, format string, expected []string) {
	tb.Helper()
	require.NoError(tb, Pipe{}.Default(ctx))
	require.NoError(tb, Pipe{}.Run(ctx))

	artifacts := ctx.Artifacts.List()
	require.Len(tb, artifacts, 1)
	require.Equal(tb, artifact.Artifact{
		Type: artifact.UploadableSourceArchive,
		Name: "foo-1.0.0." + format,
		Path: "dist/foo-1.0.0." + format,
		Extra: map[string]any{
			artifact.ExtraFormat: format,
		},
	}, *artifacts[0])
	path := filepath.Join(tmp, "dist", "foo-1.0.0."+format)
	stat, err := os.Stat(path)
	require.NoError(tb, err)
	require.Greater(tb, stat.Size(), int64(100))

	require.ElementsMatch(tb, expected, testlib.LsArchive(tb, path, format))

	version := testlib.GetFileFromArchive(tb, path, format, "foo-1.0.0/.VERSION")
	require.Equal(tb, " (HEAD -> main, tag: v1.0.0)", string(version))
}

func TestInvalidFormat(t *testing.T) {
	ctx := testctx.WrapWithCfg(t.Context(), config.Project{
		Dist:        t.TempDir(),
		ProjectName: "foo",
		Source: config.Source{
			Format:         "7z",
			Enabled:        true,
			PrefixTemplate: "{{ .ProjectName }}-{{ .Version }}/",
		},
	})

	require.NoError(t, Pipe{}.Default(ctx))
	require.EqualError(t, Pipe{}.Run(ctx), "invalid source archive format: 7z")
}

func TestDefault(t *testing.T) {
	ctx := testctx.Wrap(t.Context())
	require.NoError(t, Pipe{}.Default(ctx))
	require.Equal(t, config.Source{
		NameTemplate: "{{ .ProjectName }}-{{ .Version }}",
		Format:       "tar.gz",
	}, ctx.Config.Source)
}

func TestInvalidNameTemplate(t *testing.T) {
	ctx := testctx.WrapWithCfg(t.Context(), config.Project{
		Source: config.Source{
			Enabled:      true,
			NameTemplate: "{{ .foo }-asdda",
		},
	})

	require.NoError(t, Pipe{}.Default(ctx))
	testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
}

func TestInvalidInvalidFileTemplate(t *testing.T) {
	testlib.Mktmp(t)
	require.NoError(t, os.Mkdir("dist", 0o744))

	testlib.GitInit(t)
	require.NoError(t, os.WriteFile("code.txt", []byte("not really code"), 0o655))
	testlib.GitAdd(t)
	testlib.GitCommit(t, "feat: first")

	ctx := testctx.WrapWithCfg(t.Context(), config.Project{
		ProjectName: "foo",
		Dist:        "dist",
		Source: config.Source{
			Format:  "tar.gz",
			Enabled: true,
			Files: []config.File{
				{Source: "{{.Test}"},
			},
		},
	})

	ctx.Git.FullCommit = "HEAD"
	ctx.Version = "1.0.0"
	require.NoError(t, Pipe{}.Default(ctx))
	testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
}

func TestInvalidPrefixTemplate(t *testing.T) {
	ctx := testctx.WrapWithCfg(t.Context(), config.Project{
		Dist: t.TempDir(),
		Source: config.Source{
			Enabled:        true,
			PrefixTemplate: "{{ .ProjectName }/",
		},
	})

	require.NoError(t, Pipe{}.Default(ctx))
	testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
}

func TestDisabled(t *testing.T) {
	require.True(t, Pipe{}.Skip(testctx.Wrap(t.Context())))
}

func TestSkip(t *testing.T) {
	t.Run("skip", func(t *testing.T) {
		require.True(t, Pipe{}.Skip(testctx.Wrap(t.Context())))
	})

	t.Run("dont skip", func(t *testing.T) {
		ctx := testctx.WrapWithCfg(t.Context(), config.Project{
			Source: config.Source{
				Enabled: true,
			},
		})

		require.False(t, Pipe{}.Skip(ctx))
	})
}

func TestString(t *testing.T) {
	require.NotEmpty(t, Pipe{}.String())
}
