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

	"github.com/caapim/layer7-operator/pkg/gateway"
	networkingv1 "k8s.io/api/networking/v1"
	apiequality "k8s.io/apimachinery/pkg/api/equality"
	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 Ingress(ctx context.Context, params Params) error {
	if strings.ToLower(params.Instance.Spec.App.Ingress.Type) == "route" {
		return nil
	}

	desiredIngress := gateway.NewIngress(params.Instance)
	currentIngress := &networkingv1.Ingress{}

	if desiredIngress != nil {
		if err := controllerutil.SetControllerReference(params.Instance, desiredIngress, params.Scheme); err != nil {
			return fmt.Errorf("failed to set controller reference: %w", err)
		}
	}
	err := params.Client.Get(ctx, types.NamespacedName{Name: params.Instance.Name, Namespace: params.Instance.Namespace}, currentIngress)

	if !params.Instance.Spec.App.Ingress.Enabled && !k8serrors.IsNotFound(err) && controllerutil.HasControllerReference(currentIngress) {
		if err := params.Client.Delete(ctx, currentIngress); err != nil {
			return err
		}
		params.Log.Info("removed ingress", "name", params.Instance.Name, "namespace", params.Instance.Namespace)
		return nil
	}

	if !params.Instance.Spec.App.Ingress.Enabled {
		return nil
	}

	if err != nil && k8serrors.IsNotFound(err) {
		if err = params.Client.Create(ctx, desiredIngress); err != nil {
			return err
		}
		params.Log.Info("created ingress", "name", desiredIngress.Name, "namespace", params.Instance.Namespace)
		return nil
	}
	if err != nil {
		return err
	}

	// Ingress controllers often add annotations (e.g. Contour); require full spec match but only
	// that Gateway-owned label/annotation keys match—do not replace all metadata or we thrash.
	if apiequality.Semantic.DeepEqual(currentIngress.Spec, desiredIngress.Spec) &&
		apiequality.Semantic.DeepEqual(currentIngress.ObjectMeta.OwnerReferences, desiredIngress.ObjectMeta.OwnerReferences) &&
		metadataKeysMatch(currentIngress.ObjectMeta, desiredIngress.ObjectMeta) {
		params.Log.V(2).Info("no ingress updates needed", "name", desiredIngress.Name, "namespace", desiredIngress.Namespace)
		return nil
	}

	updatedIngress := currentIngress.DeepCopy()

	updatedIngress.ObjectMeta.OwnerReferences = desiredIngress.ObjectMeta.OwnerReferences
	updatedIngress.Spec.Rules = desiredIngress.Spec.Rules
	updatedIngress.Spec.TLS = desiredIngress.Spec.TLS
	updatedIngress.Spec.DefaultBackend = desiredIngress.Spec.DefaultBackend
	updatedIngress.Spec.IngressClassName = desiredIngress.Spec.IngressClassName

	if updatedIngress.Labels == nil {
		updatedIngress.Labels = make(map[string]string)
	}
	for k, v := range desiredIngress.Labels {
		updatedIngress.Labels[k] = v
	}
	if updatedIngress.Annotations == nil {
		updatedIngress.Annotations = make(map[string]string)
	}
	for k, v := range desiredIngress.Annotations {
		updatedIngress.Annotations[k] = v
	}

	patch := client.MergeFrom(currentIngress)
	if err := params.Client.Patch(ctx, updatedIngress, patch); err != nil {
		return err
	}
	params.Log.Info("ingress updated", "name", desiredIngress.Name, "namespace", desiredIngress.Namespace)

	return nil
}
