// Copyright 2026 Microsoft Corporation
//
// 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 creation

import (
	"context"
	"fmt"
	"time"

	"github.com/Azure/ARO-HCP/backend/pkg/utils/controllerutils"
	"github.com/Azure/ARO-HCP/internal/database/cosmosstorage/corecosmosstorage"
	"github.com/Azure/ARO-HCP/internal/database/cosmosstorage/cosmosstorageutils"
	"github.com/Azure/ARO-HCP/internal/database/informers/coreinformers"
	"github.com/Azure/ARO-HCP/internal/database/listers/corelisters"
	"github.com/Azure/ARO-HCP/internal/utils"
)

// createServiceProviderClusterSyncer ensures a ServiceProviderCluster document
// exists for every HCPCluster. Consumer backend controllers (validation,
// version, etc.) read the ServiceProviderCluster through a cached lister and
// bail out when it is missing; this syncer is the single place in backend
// controllers that actually creates the document, so the GetOrCreate pattern
// stays in one well-known location. The frontend admission path still calls
// corecosmosstorage.GetOrCreateServiceProviderCluster directly because admission must
// have the document in hand to validate the request before any controller
// has a chance to run.
type createServiceProviderClusterSyncer struct {
	resourcesDBClient            corecosmosstorage.ResourcesDBClient
	clusterLister                corelisters.ClusterLister
	serviceProviderClusterLister corelisters.ServiceProviderClusterLister
}

var _ controllerutils.ClusterSyncer = (*createServiceProviderClusterSyncer)(nil)

// NewCreateServiceProviderClusterController wires the controller that creates
// missing ServiceProviderCluster documents.
func NewCreateServiceProviderClusterController(
	resourcesDBClient corecosmosstorage.ResourcesDBClient,
	clusterLister corelisters.ClusterLister,
	serviceProviderClusterLister corelisters.ServiceProviderClusterLister,
	backendInformers coreinformers.BackendInformers,
) controllerutils.Controller {
	syncer := &createServiceProviderClusterSyncer{
		resourcesDBClient:            resourcesDBClient,
		clusterLister:                clusterLister,
		serviceProviderClusterLister: serviceProviderClusterLister,
	}

	return controllerutils.NewClusterWatchingController(
		"CreateServiceProviderCluster",
		resourcesDBClient,
		backendInformers,
		nil,
		1*time.Minute,
		syncer,
	)
}

// SyncOnce creates a ServiceProviderCluster for the given HCPCluster when one
// does not already exist. The lister is consulted first so steady-state runs
// avoid a Cosmos round-trip; if it is missing, GetOrCreate is called and any
// 409 conflict is handled by the underlying helper.
//
// Once the parent cluster is marked for deletion the cluster deletion
// pipeline removes the ServiceProviderCluster as a child resource; recreating
// it here would prevent that pipeline from converging, so we short-circuit.
func (c *createServiceProviderClusterSyncer) SyncOnce(ctx context.Context, key controllerutils.HCPClusterKey) error {
	existingCluster, err := c.clusterLister.Get(ctx, key.SubscriptionID, key.ResourceGroupName, key.HCPClusterName)
	if cosmosstorageutils.IsNotFoundError(err) {
		return nil
	}
	if err != nil {
		return utils.TrackError(fmt.Errorf("failed to get HCPCluster from lister: %w", err))
	}
	if existingCluster.ServiceProviderProperties.DeletionTimestamp != nil {
		return nil
	}

	_, err = c.serviceProviderClusterLister.Get(ctx, key.SubscriptionID, key.ResourceGroupName, key.HCPClusterName)
	if err == nil {
		return nil
	}
	if !cosmosstorageutils.IsNotFoundError(err) {
		return utils.TrackError(fmt.Errorf("failed to get ServiceProviderCluster from lister: %w", err))
	}

	if _, err := corecosmosstorage.GetOrCreateServiceProviderCluster(ctx, c.resourcesDBClient, key.GetResourceID()); err != nil {
		return utils.TrackError(fmt.Errorf("failed to create ServiceProviderCluster: %w", err))
	}

	return nil
}
