package envfile_test

import (
	"reflect"
	"slices"
	"strings"
	"testing"

	appPkg "coopcloud.tech/abra/pkg/app"
	"coopcloud.tech/abra/pkg/config"
	"coopcloud.tech/abra/pkg/envfile"
	"coopcloud.tech/abra/pkg/recipe"
	testPkg "coopcloud.tech/abra/pkg/test"
	"github.com/stretchr/testify/assert"
)

func TestGetAllFoldersInDirectory(t *testing.T) {
	folders, err := config.GetAllFoldersInDirectory(testPkg.TestFolder)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(folders, testPkg.TFolders) {
		t.Fatalf("did not get expected folders. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFolders, ","), strings.Join(folders, ","))
	}
}

func TestGetAllFilesInDirectory(t *testing.T) {
	files, err := config.GetAllFilesInDirectory(testPkg.TestFolder)
	if err != nil {
		t.Fatal(err)
	}
	var fileNames []string
	for _, file := range files {
		fileNames = append(fileNames, file.Name())
	}
	if !reflect.DeepEqual(fileNames, testPkg.TFiles) {
		t.Fatalf("did not get expected files. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFiles, ","), strings.Join(fileNames, ","))
	}
}

func TestReadEnv(t *testing.T) {
	env, err := envfile.ReadEnv(testPkg.ExpectedAppFile.Path)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(env, testPkg.ExpectedAppEnv) {
		t.Fatal("did not get expected application settings")
	}
}

func TestReadAbraShEnvVars(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	abraShEnv, err := envfile.ReadAbraShEnvVars(r.AbraShPath)
	if err != nil {
		t.Fatal(err)
	}

	if len(abraShEnv) == 0 {
		t.Error("at least one env var should be exported")
	}

	if _, ok := abraShEnv["INNER_FOO"]; ok {
		t.Error("INNER_FOO should not be exported")
	}

	if _, ok := abraShEnv["INNER_BAZ"]; ok {
		t.Error("INNER_BAZ should not be exported")
	}

	if _, ok := abraShEnv["OUTER_FOO"]; !ok {
		t.Error("OUTER_FOO should be exported")
	}
}

func TestReadAbraShCmdNames(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	cmdNames, err := appPkg.ReadAbraShCmdNames(r.AbraShPath)
	if err != nil {
		t.Fatal(err)
	}

	if len(cmdNames) == 0 {
		t.Error("at least one command name should be found")
	}

	expectedCmdNames := []string{"test_cmd", "test_cmd_args"}
	for _, cmdName := range expectedCmdNames {
		if !slices.Contains(cmdNames, cmdName) {
			t.Fatalf("%s should have been found in %s", cmdName, r.AbraShPath)
		}
	}
}

func TestCheckEnv(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	envSample, err := r.SampleEnv()
	if err != nil {
		t.Fatal(err)
	}

	app := appPkg.App{
		Name:   "test-app",
		Recipe: recipe.Get(r.Name),
		Domain: "example.com",
		Env:    envSample,
		Path:   "example.com.env",
		Server: "example.com",
	}

	envVars, err := appPkg.CheckEnv(app)
	if err != nil {
		t.Fatal(err)
	}

	for _, envVar := range envVars {
		if !envVar.Present {
			t.Fatalf("%s should be present", envVar.Name)
		}
	}
}

func TestCheckEnvError(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	envSample, err := r.SampleEnv()
	if err != nil {
		t.Fatal(err)
	}

	delete(envSample, "DOMAIN")

	app := appPkg.App{
		Name:   "test-app",
		Recipe: recipe.Get(r.Name),
		Domain: "example.com",
		Env:    envSample,
		Path:   "example.com.env",
		Server: "example.com",
	}

	envVars, err := appPkg.CheckEnv(app)
	if err != nil {
		t.Fatal(err)
	}

	for _, envVar := range envVars {
		if envVar.Name == "DOMAIN" && envVar.Present {
			t.Fatalf("%s should not be present", envVar.Name)
		}
	}
}

func TestEnvVarCommentsRemoved(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	envSample, err := r.SampleEnv()
	if err != nil {
		t.Fatal(err)
	}

	envVar, exists := envSample["WITH_COMMENT"]
	if !exists {
		t.Fatal("WITH_COMMENT env var should be present in .env.sample")
	}

	if strings.Contains(envVar, "should be removed") {
		t.Fatalf("comment from '%s' should be removed", envVar)
	}

	envVar, exists = envSample["SECRET_TEST_PASS_TWO_VERSION"]
	if !exists {
		t.Fatal("SECRET_TEST_PASS_TWO_VERSION env var should be present in .env.sample")
	}

	if strings.Contains(envVar, "length") {
		t.Fatal("comment from env var SECRET_TEST_PASS_TWO_VERSION should have been removed")
	}
}

func TestEnvVarModifiersIncluded(t *testing.T) {
	r := recipe.Get("abra-test-recipe")
	err := r.EnsureExists()
	if err != nil {
		t.Fatal(err)
	}

	envSample, modifiers, err := envfile.ReadEnvWithModifiers(r.SampleEnvPath)
	if err != nil {
		t.Fatal(err)
	}

	if !strings.Contains(envSample["SECRET_TEST_PASS_TWO_VERSION"], "v1") {
		t.Errorf("value should be 'v1', got: '%s'", envSample["SECRET_TEST_PASS_TWO_VERSION"])
	}
	if modifiers == nil || modifiers["SECRET_TEST_PASS_TWO_VERSION"] == nil {
		t.Errorf("no modifiers included")
	} else {
		if modifiers["SECRET_TEST_PASS_TWO_VERSION"]["length"] != "10" {
			t.Errorf("length modifier should be '10', got: '%s'", modifiers["SECRET_TEST_PASS_TWO_VERSION"]["length"])
		}
	}
}

func TestNoOverwriteNonVersionEnvVars(t *testing.T) {
	app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
	if err != nil {
		t.Fatal(err)
	}

	if err := app.WriteRecipeVersion("1.3.12", true); err != nil {
		t.Fatal(err)
	}

	app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
	if err != nil {
		t.Fatal(err)
	}

	assert.NotEqual(t, app.Env["SMTP_AUTHTYPE"], "login:1.3.12")
}