package test_test

import (
	"path/filepath"
	"testing"

	"github.com/gruntwork-io/terragrunt/internal/cli/commands/catalog/tui/command"
	"github.com/gruntwork-io/terragrunt/internal/configbridge"
	"github.com/gruntwork-io/terragrunt/internal/services/catalog"
	"github.com/gruntwork-io/terragrunt/internal/services/catalog/module"
	"github.com/gruntwork-io/terragrunt/internal/util"
	"github.com/gruntwork-io/terragrunt/internal/vfs"
	"github.com/gruntwork-io/terragrunt/pkg/config"
	"github.com/gruntwork-io/terragrunt/pkg/options"
	"github.com/gruntwork-io/terragrunt/test/helpers"
	"github.com/gruntwork-io/terragrunt/test/helpers/logger"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	testFixtureCatalogLocalTemplate = "fixtures/catalog/local-template"
)

func TestCatalogGitRepoUpdate(t *testing.T) {
	t.Parallel()

	ctx := t.Context()

	tempDir := helpers.TmpDirWOSymlinks(t)

	_, err := module.NewRepo(ctx, logger.CreateLogger(), vfs.NewOSFS(), &module.RepoOpts{CloneURL: "github.com/gruntwork-io/terraform-fake-modules.git", Path: tempDir})
	require.NoError(t, err)

	_, err = module.NewRepo(ctx, logger.CreateLogger(), vfs.NewOSFS(), &module.RepoOpts{CloneURL: "github.com/gruntwork-io/terraform-fake-modules.git", Path: tempDir})
	require.NoError(t, err)
}

func TestScaffoldGitRepo(t *testing.T) {
	t.Parallel()

	ctx := t.Context()

	tempDir := helpers.TmpDirWOSymlinks(t)

	repo, err := module.NewRepo(ctx, logger.CreateLogger(), vfs.NewOSFS(), &module.RepoOpts{CloneURL: "github.com/gruntwork-io/terraform-fake-modules.git", Path: tempDir})
	require.NoError(t, err)

	modules, err := repo.FindModules(ctx, logger.CreateLogger(), vfs.NewOSFS())
	require.NoError(t, err)
	assert.Len(t, modules, 4)
}

func TestScaffoldGitModule(t *testing.T) {
	t.Parallel()

	ctx := t.Context()

	tempDir := helpers.TmpDirWOSymlinks(t)

	repo, err := module.NewRepo(ctx, logger.CreateLogger(), vfs.NewOSFS(), &module.RepoOpts{CloneURL: "https://github.com/gruntwork-io/terraform-fake-modules.git", Path: tempDir})
	require.NoError(t, err)

	modules, err := repo.FindModules(ctx, logger.CreateLogger(), vfs.NewOSFS())
	require.NoError(t, err)

	var auroraModule *module.Module

	for _, m := range modules {
		if m.Title() == "Terraform Fake AWS Aurora Module" {
			auroraModule = m
		}
	}

	assert.NotNil(t, auroraModule)

	testPath := helpers.TmpDirWOSymlinks(t)
	opts, err := options.NewTerragruntOptionsForTest(testPath)
	require.NoError(t, err)

	opts.ScaffoldVars = []string{"EnableRootInclude=false"}

	svc := catalog.NewCatalogService(opts).WithRepoURL("https://github.com/gruntwork-io/terraform-fake-modules.git")
	cmd := command.NewScaffold(createLogger(), opts, svc, auroraModule)
	err = cmd.Run()
	require.NoError(t, err)

	cfg := readConfig(t, opts)
	assert.NotEmpty(t, cfg.Inputs)
	assert.Len(t, cfg.Inputs, 1)
	_, found := cfg.Inputs["vpc_id"]
	assert.True(t, found)
	assert.Contains(t, *cfg.Terraform.Source, "git::https://github.com/gruntwork-io/terraform-fake-modules.git//modules/aws/aurora")
}

func TestScaffoldGitModuleHttps(t *testing.T) {
	t.Parallel()

	ctx := t.Context()

	tempDir := helpers.TmpDirWOSymlinks(t)

	repo, err := module.NewRepo(
		ctx,
		logger.CreateLogger(),
		vfs.NewOSFS(),
		&module.RepoOpts{
			CloneURL: "https://github.com/gruntwork-io/terraform-fake-modules",
			Path:     tempDir,
		},
	)
	require.NoError(t, err)

	modules, err := repo.FindModules(ctx, logger.CreateLogger(), vfs.NewOSFS())
	require.NoError(t, err)

	var auroraModule *module.Module

	for _, m := range modules {
		if m.Title() == "Terraform Fake AWS Aurora Module" {
			auroraModule = m
		}
	}

	assert.NotNil(t, auroraModule)

	testPath := helpers.TmpDirWOSymlinks(t)
	opts, err := options.NewTerragruntOptionsForTest(testPath)
	require.NoError(t, err)

	opts.ScaffoldVars = []string{"EnableRootInclude=false"}

	svc := catalog.NewCatalogService(opts).WithRepoURL("https://github.com/gruntwork-io/terraform-fake-modules")
	cmd := command.NewScaffold(createLogger(), opts, svc, auroraModule)
	err = cmd.Run()
	require.NoError(t, err)

	cfg := readConfig(t, opts)
	assert.NotEmpty(t, cfg.Inputs)
	assert.Len(t, cfg.Inputs, 1)
	_, found := cfg.Inputs["vpc_id"]
	assert.True(t, found)
	assert.Contains(t, *cfg.Terraform.Source, "git::https://github.com/gruntwork-io/terraform-fake-modules.git//modules/aws/aurora?ref=v0.0.5")

	helpers.RunTerragrunt(t, "terragrunt init --non-interactive --working-dir "+opts.WorkingDir)
}

func TestCatalogWithLocalDefaultTemplate(t *testing.T) {
	t.Parallel()

	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureCatalogLocalTemplate, ".boilerplate")
	helpers.CleanupTerraformFolder(t, tmpEnvPath)
	rootPath := filepath.Join(tmpEnvPath, testFixtureCatalogLocalTemplate)

	targetPath := filepath.Join(rootPath, "app")
	moduleURL := "github.com/gruntwork-io/terragrunt//test/fixtures/inputs"

	_, _, err := helpers.RunTerragruntCommandWithOutput(
		t,
		"terragrunt scaffold --non-interactive --working-dir "+targetPath+" "+moduleURL,
	)

	require.NoError(t, err)
	assert.FileExists(t, filepath.Join(targetPath, "terragrunt.hcl"))
	assert.FileExists(t, filepath.Join(targetPath, "custom-template.txt"))

	content, err := util.ReadFileAsString(filepath.Join(targetPath, "terragrunt.hcl"))
	require.NoError(t, err)
	assert.Contains(t, content, "# Custom local template")
}

func readConfig(t *testing.T, opts *options.TerragruntOptions) *config.TerragruntConfig {
	t.Helper()

	assert.FileExists(t, opts.WorkingDir+"/terragrunt.hcl")

	opts, err := options.NewTerragruntOptionsForTest(filepath.Join(opts.WorkingDir, "terragrunt.hcl"))
	require.NoError(t, err)

	l := logger.CreateLogger()
	_, pctx := configbridge.NewParsingContext(t.Context(), l, opts)
	cfg, err := config.ReadTerragruntConfig(t.Context(), l, pctx, config.DefaultParserOptions(l, opts.StrictControls))
	require.NoError(t, err)

	return cfg
}
