/*
* 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"
	"reflect"

	securityv1alpha1 "github.com/caapim/layer7-operator/api/v1alpha1"
	"github.com/caapim/layer7-operator/pkg/util"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
)

func RedisStateStore(ctx context.Context, params Params) error {
	statestore := params.Instance
	status := statestore.Status

	// Retrieve existing secret for Redis
	// this will need to be updated for multi-state store provider support
	if statestore.Spec.Redis.ExistingSecret != "" {
		stateStoreSecret, err := getStateStoreSecret(ctx, statestore.Spec.Redis.ExistingSecret, *statestore, params)
		if err != nil {
			params.Log.V(2).Info("failed to retrieve credential secret", "name", statestore.Name, "namespace", statestore.Namespace, "message", err.Error())
			status.Ready = false
			statusErr := updateStatus(ctx, params, status)
			if statusErr != nil {
				return statusErr
			}
			return err
		}
		statestore.Spec.Redis.Username = string(stateStoreSecret.Data["username"])
		statestore.Spec.Redis.MasterPassword = string(stateStoreSecret.Data["masterPassword"])
	}

	c, err := util.RedisClient(&statestore.Spec.Redis)
	if err != nil {
		params.Recorder.Eventf(statestore, "Warning", "ConnectionFailed", "%s in namespace %s", statestore.Name, statestore.Namespace)
		params.Log.V(2).Info("failed to connect to state store", "name", statestore.Name, "namespace", statestore.Namespace, "message", err.Error())
		status.Ready = false
		statusErr := updateStatus(ctx, params, status)
		if statusErr != nil {
			return statusErr
		}
		return err
	}
	defer c.Close()

	ping := c.Ping(ctx)

	pong, err := ping.Result()
	if err != nil {
		if status.Ready {
			params.Recorder.Eventf(statestore, "Warning", "ConnectionFailed", "%s in namespace %s", statestore.Name, statestore.Namespace)
			status.Ready = false
		}
		params.Log.V(2).Info("failed to connect to state store", "name", statestore.Name, "namespace", statestore.Namespace, "message", err.Error())

		statusErr := updateStatus(ctx, params, status)
		if statusErr != nil {
			return statusErr
		}
		return err
	}

	if pong == "PONG" {
		if !status.Ready {
			params.Recorder.Eventf(statestore, "Normal", "ConnectionSuccess", "Successfully connected to l7statestore %s in namespace %s", statestore.Name, statestore.Namespace)
			status.Ready = true
		}
	}

	statusErr := updateStatus(ctx, params, status)
	if statusErr != nil {
		return statusErr
	}
	return nil
}

func getStateStoreSecret(ctx context.Context, name string, statestore securityv1alpha1.L7StateStore, params Params) (*corev1.Secret, error) {
	statestoreSecret := &corev1.Secret{}

	err := params.Client.Get(ctx, types.NamespacedName{Name: name, Namespace: statestore.Namespace}, statestoreSecret)
	if err != nil {
		return statestoreSecret, err
	}
	return statestoreSecret, nil
}

func updateStatus(ctx context.Context, params Params, status securityv1alpha1.L7StateStoreStatus) error {
	if !reflect.DeepEqual(params.Instance, status) {
		params.Instance.Status = status
		err := params.Client.Status().Update(ctx, params.Instance)
		if err != nil {
			params.Log.V(2).Info("failed to update state store status", "name", params.Instance.Name, "namespace", params.Instance.Namespace, "message", err.Error())
			return err
		}
		params.Log.V(2).Info("updated state store status", "name", params.Instance.Name, "namespace", params.Instance.Namespace)
	}
	return nil
}
