package image

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/replicate/cog/pkg/config"
)

var hasGit = (func() bool {
	_, err := exec.LookPath("git")
	return err == nil
})()

func gitRun(ctx context.Context, argv []string, t *testing.T) {
	ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
	t.Cleanup(cancel)

	out, err := exec.CommandContext(ctx, "git", argv...).CombinedOutput()
	t.Logf("git output:\n%s", string(out))

	require.NoError(t, err)
}

func setupGitWorkTree(t *testing.T) string {
	ctx := t.Context()
	if !hasGit {
		t.Skip("no git executable available")
		return ""
	}

	r := require.New(t)

	tmp := filepath.Join(t.TempDir(), "wd")
	r.NoError(os.MkdirAll(tmp, 0o755))

	gitRun(ctx, []string{"init", tmp}, t)
	gitRun(ctx, []string{"-C", tmp, "config", "user.email", "cog@localhost"}, t)
	gitRun(ctx, []string{"-C", tmp, "config", "user.name", "Cog Tests"}, t)
	gitRun(ctx, []string{"-C", tmp, "commit", "--allow-empty", "-m", "walrus"}, t)
	gitRun(ctx, []string{"-C", tmp, "tag", "-a", "v0.0.1+walrus", "-m", "walrus time"}, t)

	return tmp
}

func TestIsGitWorkTree(t *testing.T) {
	ctx := t.Context()
	r := require.New(t)

	r.False(isGitWorkTree(ctx, "/dev/null"))
	r.True(isGitWorkTree(ctx, setupGitWorkTree(t)))
}

func TestGitHead(t *testing.T) {
	t.Run("via github env", func(t *testing.T) {
		t.Setenv("GITHUB_SHA", "fafafaf")

		head, err := gitHead(t.Context(), "/dev/null")

		require.NoError(t, err)
		require.Equal(t, "fafafaf", head)
	})

	t.Run("via git", func(t *testing.T) {
		tmp := setupGitWorkTree(t)
		if tmp == "" {
			return
		}

		t.Setenv("GITHUB_SHA", "")

		head, err := gitHead(t.Context(), tmp)
		require.NoError(t, err)
		require.NotEqual(t, "", head)
	})

	t.Run("unavailable", func(t *testing.T) {
		t.Setenv("GITHUB_SHA", "")

		head, err := gitHead(t.Context(), "/dev/null")
		require.Error(t, err)
		require.Equal(t, "", head)
	})
}

func TestGitTag(t *testing.T) {
	t.Run("via github env", func(t *testing.T) {
		t.Setenv("GITHUB_REF_NAME", "v0.0.1+manatee")

		tag, err := gitTag(t.Context(), "/dev/null")
		require.NoError(t, err)
		require.Equal(t, "v0.0.1+manatee", tag)
	})

	t.Run("via git", func(t *testing.T) {
		tmp := setupGitWorkTree(t)
		if tmp == "" {
			return
		}

		t.Setenv("GITHUB_REF_NAME", "")

		tag, err := gitTag(t.Context(), tmp)
		require.NoError(t, err)
		require.Equal(t, "v0.0.1+walrus", tag)
	})

	t.Run("unavailable", func(t *testing.T) {
		t.Setenv("GITHUB_REF_NAME", "")

		tag, err := gitTag(t.Context(), "/dev/null")
		require.Error(t, err)
		require.Equal(t, "", tag)
	})
}

func TestCanUseStaticSchemaGen(t *testing.T) {
	// Helper to build a config with a specific SDK version.
	cfgWithSDK := func(version string) *config.Config {
		return &config.Config{
			Build: &config.Build{SDKVersion: version},
		}
	}
	noBuild := &config.Config{}

	tests := []struct {
		name     string
		cfg      *config.Config
		envVar   string // COG_STATIC_SCHEMA value
		sdkWheel string // COG_SDK_WHEEL value
		want     bool
	}{
		{
			name: "disabled by default (env not set)",
			cfg:  cfgWithSDK("0.18.0"),
			want: false,
		},
		{
			name:   "disabled when env is empty string",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "",
			want:   false,
		},
		{
			name:   "disabled when env is 0",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "0",
			want:   false,
		},
		{
			name:   "enabled when env is 1",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "1",
			want:   true,
		},
		{
			name:   "enabled when env is true",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "true",
			want:   true,
		},
		{
			name:   "enabled when env is True (mixed case)",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "True",
			want:   true,
		},
		{
			name:   "enabled when env is TRUE (upper case)",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "TRUE",
			want:   true,
		},
		{
			name:   "disabled for old SDK even when opted in",
			cfg:    cfgWithSDK("0.16.12"),
			envVar: "1",
			want:   false,
		},
		{
			name:   "disabled for pre-release old SDK",
			cfg:    cfgWithSDK("0.16.0a1"),
			envVar: "1",
			want:   false,
		},
		{
			name:   "enabled for SDK 0.17.0 when opted in",
			cfg:    cfgWithSDK("0.17.0"),
			envVar: "1",
			want:   true,
		},
		{
			name:   "enabled for SDK 0.18.0 when opted in",
			cfg:    cfgWithSDK("0.18.0"),
			envVar: "1",
			want:   true,
		},
		{
			name:   "enabled for unpinned SDK when opted in",
			cfg:    noBuild,
			envVar: "1",
			want:   true,
		},
		{
			name:     "disabled for old SDK via COG_SDK_WHEEL even when opted in",
			cfg:      noBuild,
			envVar:   "1",
			sdkWheel: "pypi:0.16.12",
			want:     false,
		},
		{
			name:     "enabled for new SDK via COG_SDK_WHEEL when opted in",
			cfg:      noBuild,
			envVar:   "1",
			sdkWheel: "pypi:0.18.0",
			want:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Setenv("COG_STATIC_SCHEMA", tt.envVar)
			t.Setenv("COG_SDK_WHEEL", tt.sdkWheel)

			got := canUseStaticSchemaGen(tt.cfg)
			require.Equal(t, tt.want, got)
		})
	}
}
