package secret

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestReadSecretsConfig(t *testing.T) {
	composeFiles := []string{"./testdir/compose.yaml"}
	secretsFromConfig, err := ReadSecretsConfig("./testdir/.env.sample", composeFiles, "test_example_com")
	if err != nil {
		t.Fatal(err)
	}

	// Simple secret
	assert.Equal(t, "test_example_com_test_pass_one_v2", secretsFromConfig["test_pass_one"].RemoteName)
	assert.Equal(t, "v2", secretsFromConfig["test_pass_one"].Version)
	assert.Equal(t, 0, secretsFromConfig["test_pass_one"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", secretsFromConfig["test_pass_one"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_one"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_one"].Prefix)

	// Has a length modifier
	assert.Equal(t, "test_example_com_test_pass_two_v1", secretsFromConfig["test_pass_two"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_two"].Version)
	assert.Equal(t, 10, secretsFromConfig["test_pass_two"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", secretsFromConfig["test_pass_two"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_two"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_two"].Prefix)

	// Secret name does not include the secret id
	assert.Equal(t, "test_example_com_pass_three_v2", secretsFromConfig["test_pass_three"].RemoteName)
	assert.Equal(t, "v2", secretsFromConfig["test_pass_three"].Version)
	assert.Equal(t, 0, secretsFromConfig["test_pass_three"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", secretsFromConfig["test_pass_three"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_three"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_three"].Prefix)

	// Has a length modifier and a charset=default,safespecial modifier
	assert.Equal(t, "test_example_com_test_pass_four_v1", secretsFromConfig["test_pass_four"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_four"].Version)
	assert.Equal(t, 12, secretsFromConfig["test_pass_four"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#%^&*_-+=", secretsFromConfig["test_pass_four"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_four"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_four"].Prefix)

	// Has a length modifier and a charset=default,special modifier
	assert.Equal(t, "test_example_com_test_pass_five_v1", secretsFromConfig["test_pass_five"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_five"].Version)
	assert.Equal(t, 12, secretsFromConfig["test_pass_five"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%^&*_-+=", secretsFromConfig["test_pass_five"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_five"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_five"].Prefix)

	// Has only a charset=default,special modifier, which gets setted but ignored in the generation
	assert.Equal(t, "test_example_com_test_pass_six_v1", secretsFromConfig["test_pass_six"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_six"].Version)
	assert.Equal(t, 0, secretsFromConfig["test_pass_six"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%^&*_-+=", secretsFromConfig["test_pass_six"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_six"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_six"].Prefix)

	// Has a length modifier and a charset=hex modifier
	assert.Equal(t, "test_example_com_test_pass_seven_v1", secretsFromConfig["test_pass_seven"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_seven"].Version)
	assert.Equal(t, 32, secretsFromConfig["test_pass_seven"].Length)
	assert.Equal(t, "0123456789abcdef", secretsFromConfig["test_pass_seven"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_seven"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_seven"].Prefix)

	// Has a length modifier and an encoding=base64 modifier
	assert.Equal(t, "test_example_com_test_pass_eight_v1", secretsFromConfig["test_pass_eight"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_eight"].Version)
	assert.Equal(t, 12, secretsFromConfig["test_pass_eight"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", secretsFromConfig["test_pass_eight"].Charset)
	assert.Equal(t, "base64", secretsFromConfig["test_pass_eight"].Encoding)
	assert.Equal(t, "", secretsFromConfig["test_pass_eight"].Prefix)

	// Has a length modifier and a prefix=base64: modifier
	assert.Equal(t, "test_example_com_test_pass_nine_v1", secretsFromConfig["test_pass_nine"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_nine"].Version)
	assert.Equal(t, 16, secretsFromConfig["test_pass_nine"].Length)
	assert.Equal(t, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", secretsFromConfig["test_pass_nine"].Charset)
	assert.Equal(t, "", secretsFromConfig["test_pass_nine"].Encoding)
	assert.Equal(t, "base64:", secretsFromConfig["test_pass_nine"].Prefix)

	// Has all modifiers: length, charset=bytes, and prefix=base64: (Laravel-style)
	assert.Equal(t, "test_example_com_test_pass_ten_v1", secretsFromConfig["test_pass_ten"].RemoteName)
	assert.Equal(t, "v1", secretsFromConfig["test_pass_ten"].Version)
	assert.Equal(t, 32, secretsFromConfig["test_pass_ten"].Length)
	assert.Equal(t, "bytes", secretsFromConfig["test_pass_ten"].Charset)
	assert.Equal(t, "base64", secretsFromConfig["test_pass_ten"].Encoding) // Defaults to base64 for bytes
	assert.Equal(t, "base64:", secretsFromConfig["test_pass_ten"].Prefix)
}

func TestReadSecretsConfigWithLongDomain(t *testing.T) {
	composeFiles := []string{"./testdir/compose.yaml"}
	_, err := ReadSecretsConfig("./testdir/.env.sample", composeFiles, "should_break_on_forty_eight_char_stack_nameeeeee")
	if err == nil {
		t.Fatal("expected failure, stack name is too long")
	}
	assert.Contains(t, err.Error(), "is > 64 chars")
}

func TestEncodeSecret(t *testing.T) {
	// base64 encoding
	input := "testpassword123"
	encoded := encodeSecret(input, "base64")
	expected := "dGVzdHBhc3N3b3JkMTIz"
	assert.Equal(t, expected, encoded)

	// no encoding (default)
	noEncoding := encodeSecret(input, "")
	assert.Equal(t, input, noEncoding)

	// unknown encoding (should return original)
	unknownEncoding := encodeSecret(input, "unknown")
	assert.Equal(t, input, unknownEncoding)
}

func TestApplyPrefix(t *testing.T) {
	input := "testvalue"

	// with prefix
	prefixed := applyPrefix(input, "base64:")
	assert.Equal(t, "base64:testvalue", prefixed)

	// with empty prefix
	noPrefixed := applyPrefix(input, "")
	assert.Equal(t, input, noPrefixed)
}

func TestGenerateRandomBytes(t *testing.T) {
	// random bytes generation with 32 bytes
	key, err := generateRandomBytes(32)
	assert.NoError(t, err)
	assert.Equal(t, 32, len([]byte(key))) // Check raw byte length

	// random bytes generation with 16 bytes
	key16, err := generateRandomBytes(16)
	assert.NoError(t, err)
	assert.Equal(t, 16, len([]byte(key16))) // Check raw byte length

	// that keys are different (randomness)
	key2, err := generateRandomBytes(32)
	assert.NoError(t, err)
	assert.NotEqual(t, key, key2)
}
