// Copyright 2022 Camunda Services GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package camunda

import (
	"path/filepath"
	"strings"
	"testing"

	"github.com/gruntwork-io/terratest/modules/helm"
	"github.com/gruntwork-io/terratest/modules/k8s"
	"github.com/gruntwork-io/terratest/modules/random"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

type shouldAutogenerateSecretTest struct {
	suite.Suite
	chartPath string
	release   string
	namespace string
}

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

	chartPath, err := filepath.Abs("../../../")
	require.NoError(t, err)

	suite.Run(t, &shouldAutogenerateSecretTest{
		chartPath: chartPath,
		release:   "test",
		namespace: "test-" + strings.ToLower(random.UniqueId()),
	})
}

// Test helper to check if a secret key is generated (which indicates shouldAutogenerateSecret returned true)
func (s *shouldAutogenerateSecretTest) shouldSecretKeyBeGenerated(config map[string]string, expectedSecretKey string) bool {
	// Add the base config to enable secret generation
	baseConfig := map[string]string{
		"global.secrets.autoGenerated": "true",
	}
	for k, v := range config {
		baseConfig[k] = v
	}
	
	options := &helm.Options{
		KubectlOptions: k8s.NewKubectlOptions("", "", s.namespace),
		SetValues:      baseConfig,
	}

	// Render the secret template
	output, err := helm.RenderTemplateE(s.T(), options, s.chartPath, s.release, []string{"templates/common/secret-camunda.yaml"})
	if err != nil {
		// If template fails to render, the secret is not generated
		return false
	}
	
	// Check if the expected secret key is in the output
	return strings.Contains(output, expectedSecretKey+":") 
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithExistingSecretString() {
	// Test case: component has existingSecret as string pointing to autogen secret
	config := map[string]string{
		"identity.firstUser.existingSecret": "camunda-credentials",
		"global.identity.auth.enabled":    "true",
	}
	
	// Should generate the identity-firstuser-password key
	result := s.shouldSecretKeyBeGenerated(config, "identity-firstuser-password")
	s.Require().True(result, "identity-firstuser-password should be generated when identity.firstUser.existingSecret points to autogen secret")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithExistingSecretObject() {
	// Test case: component has existingSecret as object with name pointing to autogen secret
	config := map[string]string{
		"global.identity.auth.console.existingSecret.name": "camunda-credentials",
		"global.identity.auth.enabled": "true",
	}
	
	// Should generate the identity-console-client-token key
	result := s.shouldSecretKeyBeGenerated(config, "identity-console-client-token")
	s.Require().True(result, "identity-console-client-token should be generated when global.identity.auth.console.existingSecret.name points to autogen secret")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithNewStyleSecret() {
	// Test case: component has new style secret.existingSecret pointing to autogen secret
	config := map[string]string{
		"connectors.security.authentication.oidc.secret.existingSecret": "camunda-credentials",
		"global.identity.auth.enabled": "true",
	}
	
	// Should generate the identity-connectors-client-token key (since connectors auth still uses identity)
	result := s.shouldSecretKeyBeGenerated(config, "identity-connectors-client-token")
	s.Require().True(result, "identity-connectors-client-token should be generated when using new style secret config")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithAuthExistingSecret() {
	// Test case: component has auth.existingSecret pointing to autogen secret (for identityKeycloak.auth)
	config := map[string]string{
		"identityKeycloak.auth.existingSecret": "camunda-credentials",
		"identityKeycloak.auth.passwordSecretKey": "identity-keycloak-admin-password",
		"global.identity.auth.enabled": "true",
	}
	
	// Should generate the identity-keycloak-admin-password key
	result := s.shouldSecretKeyBeGenerated(config, "identity-keycloak-admin-password")
	s.Require().True(result, "identity-keycloak-admin-password should be generated when identityKeycloak.auth.existingSecret points to autogen secret")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithPostgreSQLAuth() {
	// Test case: component has postgresql.auth.existingSecret pointing to autogen secret
	config := map[string]string{
		"identityKeycloak.postgresql.auth.existingSecret": "camunda-credentials",
		"identityKeycloak.postgresql.auth.secretKeys.adminPasswordKey": "identity-keycloak-postgresql-admin-password",
		"identityKeycloak.postgresql.auth.secretKeys.userPasswordKey": "identity-keycloak-postgresql-user-password",
	}
	
	// Should generate both PostgreSQL password keys
	result1 := s.shouldSecretKeyBeGenerated(config, "identity-keycloak-postgresql-admin-password")
	result2 := s.shouldSecretKeyBeGenerated(config, "identity-keycloak-postgresql-user-password")
	s.Require().True(result1, "identity-keycloak-postgresql-admin-password should be generated when postgresql.auth.existingSecret points to autogen secret")
	s.Require().True(result2, "identity-keycloak-postgresql-user-password should be generated when postgresql.auth.existingSecret points to autogen secret")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithDifferentSecretName() {
	// Test case: component points to different secret name - should not autogenerate
	config := map[string]string{
		"identity.firstUser.existingSecret": "my-custom-secret",
	}
	
	// Should NOT generate the identity-firstuser-password key
	result := s.shouldSecretKeyBeGenerated(config, "identity-firstuser-password")
	s.Require().False(result, "identity-firstuser-password should NOT be generated when existingSecret points to different secret name")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithNoSecretConfig() {
	// Test case: component has no secret configuration - should autogenerate
	config := map[string]string{
		// No secret configuration for identity.firstUser, so it should autogenerate
		"global.identity.auth.enabled": "true",
	}
	
	// Should generate the identity-firstuser-password key (default behavior)
	result := s.shouldSecretKeyBeGenerated(config, "identity-firstuser-password")
	s.Require().True(result, "identity-firstuser-password should be generated when no secret config is present")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithPlaintextPassword() {
	// Test case: component has plaintext password - should not autogenerate
	config := map[string]string{
		"identity.firstUser.password": "my-plaintext-password",
	}
	
	// Should NOT generate the identity-firstuser-password key because plaintext password exists
	result := s.shouldSecretKeyBeGenerated(config, "identity-firstuser-password")
	s.Require().False(result, "identity-firstuser-password should NOT be generated when plaintext password is configured")
}

func (s *shouldAutogenerateSecretTest) TestShouldAutogenerateSecretWithCustomSecretName() {
	// Test case: using custom autogenerated secret name
	config := map[string]string{
		"global.secrets.name": "my-custom-autogen-secret",
		"identity.firstUser.existingSecret": "my-custom-autogen-secret",
		"global.identity.auth.enabled": "true",
	}
	
	// Should generate the identity-firstuser-password key in the custom-named secret
	result := s.shouldSecretKeyBeGenerated(config, "identity-firstuser-password")
	s.Require().True(result, "identity-firstuser-password should be generated when existingSecret matches custom autogen secret name")
}