/*
* Copyright (c) 2026 Broadcom. All rights reserved.
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
* All trademarks, trade names, service marks, and logos referenced
* herein belong to their respective companies.
*
* This software and all information contained therein is confidential
* and proprietary and shall not be duplicated, used, disclosed or
* disseminated in any way except as authorized by the applicable
* license agreement, without the express written permission of Broadcom.
* All authorized reproductions must be marked with this language.
*
* EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO THE
* EXTENT PERMITTED BY APPLICABLE LAW OR AS AGREED BY BROADCOM IN ITS
* APPLICABLE LICENSE AGREEMENT, BROADCOM PROVIDES THIS DOCUMENTATION
* "AS IS" WITHOUT WARRANTY OF ANY KIND, INCLUDING WITHOUT LIMITATION,
* ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR. NONINFRINGEMENT. IN NO EVENT WILL BROADCOM BE LIABLE TO
* THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR DAMAGE, DIRECT OR
* INDIRECT, FROM THE USE OF THIS DOCUMENTATION, INCLUDING WITHOUT LIMITATION,
* LOST PROFITS, LOST INVESTMENT, BUSINESS INTERRUPTION, GOODWILL, OR
* LOST DATA, EVEN IF BROADCOM IS EXPRESSLY ADVISED IN ADVANCE OF THE
* POSSIBILITY OF SUCH LOSS OR DAMAGE.
*
* AI assistance has been used to generate some or all contents of this file. That includes, but is not limited to, new code, modifying existing code, stylistic edits.
 */
package reconcile

import (
	"context"
	"fmt"

	securityv1 "github.com/caapim/layer7-operator/api/v1"
	"github.com/caapim/layer7-operator/pkg/gateway"
	corev1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

func ConfigMaps(ctx context.Context, params Params) error {
	repositoryInitOpts := gateway.ConfigMapOpts{
		StorageSecretExists: func(name string) bool {
			ok, err := storageSecretAvailable(ctx, params, name)
			if err != nil {
				params.Log.V(2).Info("storage secret check failed for repository init config", "secret", name, "error", err.Error())
				return false
			}
			return ok
		},
	}
	desiredConfigMaps := []*corev1.ConfigMap{
		gateway.NewConfigMap(params.Instance, params.Instance.Name),
		gateway.NewConfigMap(params.Instance, params.Instance.Name+"-system"),
		gateway.NewConfigMap(params.Instance, params.Instance.Name+"-repository-init-config", repositoryInitOpts),
		gateway.NewConfigMap(params.Instance, params.Instance.Name+"-gateway-files"),
	}

	if params.Instance.Spec.App.ClusterProperties.Enabled {
		desiredConfigMaps = append(desiredConfigMaps, gateway.NewConfigMap(params.Instance, params.Instance.Name+"-cwp-bundle"))
	}

	if params.Instance.Spec.App.ListenPorts.Harden || params.Instance.Spec.App.ListenPorts.Custom.Enabled {
		desiredConfigMaps = append(desiredConfigMaps, gateway.NewConfigMap(params.Instance, params.Instance.Name+"-listen-port-bundle"))
	}

	if params.Instance.Spec.App.Otk.Enabled {
		desiredConfigMaps = append(desiredConfigMaps, gateway.NewConfigMap(params.Instance, params.Instance.Name+"-otk-install-init-config"))
		desiredConfigMaps = append(desiredConfigMaps, gateway.NewConfigMap(params.Instance, params.Instance.Name+"-otk-shared-init-config"))
	}

	if params.Instance.Spec.App.Otk.Database.Type == securityv1.OtkDatabaseTypeMySQL || params.Instance.Spec.App.Otk.Database.Type == securityv1.OtkDatabaseTypeOracle {
		if params.Instance.Spec.App.Otk.Database.Sql.ManageSchema {
			desiredConfigMaps = append(desiredConfigMaps, gateway.NewConfigMap(params.Instance, params.Instance.Name+"-otk-db-init-config"))
		}
	}

	if err := reconcileConfigMaps(ctx, params, desiredConfigMaps); err != nil {
		return fmt.Errorf("failed to reconcile configMaps: %w", err)
	}

	return nil
}

func reconcileConfigMaps(ctx context.Context, params Params, desiredConfigMaps []*corev1.ConfigMap) error {
	for _, dcm := range desiredConfigMaps {
		desiredConfigMap := dcm
		if err := controllerutil.SetControllerReference(params.Instance, desiredConfigMap, params.Scheme); err != nil {
			return fmt.Errorf("failed to set controller reference: %w", err)
		}

		currentConfigMap := corev1.ConfigMap{}

		err := params.Client.Get(ctx, types.NamespacedName{Name: desiredConfigMap.Name, Namespace: params.Instance.Namespace}, &currentConfigMap)
		if err != nil && k8serrors.IsNotFound(err) {
			if err = params.Client.Create(ctx, desiredConfigMap); err != nil {
				return err
			}
			params.Log.Info("created configMap", "name", desiredConfigMap.Name, "namespace", params.Instance.Namespace)
			continue
		}
		if err != nil {
			return err
		}

		if desiredConfigMap.ObjectMeta.Annotations["checksum/data"] != currentConfigMap.ObjectMeta.Annotations["checksum/data"] {
			patch := client.MergeFrom(&currentConfigMap)
			if err := params.Client.Patch(ctx, desiredConfigMap, patch); err != nil {
				return err
			}
			params.Log.V(2).Info("configMap updated", "name", desiredConfigMap.Name, "namespace", desiredConfigMap.Namespace)
		}
	}

	return nil
}
