// 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"
)

// createServiceProviderNodePoolSyncer ensures a ServiceProviderNodePool
// document exists for every HCPNodePool. Consumer backend controllers
// (validation, version, upgrade) read the ServiceProviderNodePool 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.GetOrCreateServiceProviderNodePool
// directly because admission must have the document in hand to validate the
// request before any controller has a chance to run.
type createServiceProviderNodePoolSyncer struct {
	resourcesDBClient             corecosmosstorage.ResourcesDBClient
	nodePoolLister                corelisters.NodePoolLister
	serviceProviderNodePoolLister corelisters.ServiceProviderNodePoolLister
}

var _ controllerutils.NodePoolSyncer = (*createServiceProviderNodePoolSyncer)(nil)

// NewCreateServiceProviderNodePoolController wires the controller that creates
// missing ServiceProviderNodePool documents.
func NewCreateServiceProviderNodePoolController(
	resourcesDBClient corecosmosstorage.ResourcesDBClient,
	nodePoolLister corelisters.NodePoolLister,
	serviceProviderNodePoolLister corelisters.ServiceProviderNodePoolLister,
	backendInformers coreinformers.BackendInformers,
) controllerutils.Controller {
	syncer := &createServiceProviderNodePoolSyncer{
		resourcesDBClient:             resourcesDBClient,
		nodePoolLister:                nodePoolLister,
		serviceProviderNodePoolLister: serviceProviderNodePoolLister,
	}

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

// SyncOnce creates a ServiceProviderNodePool for the given HCPNodePool 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 node pool is marked for deletion the node-pool deletion
// pipeline removes the ServiceProviderNodePool as a child resource;
// recreating it here would prevent that pipeline from converging, so we
// short-circuit.
func (c *createServiceProviderNodePoolSyncer) SyncOnce(ctx context.Context, key controllerutils.HCPNodePoolKey) error {
	existingNodePool, err := c.nodePoolLister.Get(ctx, key.SubscriptionID, key.ResourceGroupName, key.HCPClusterName, key.HCPNodePoolName)
	if cosmosstorageutils.IsNotFoundError(err) {
		return nil
	}
	if err != nil {
		return utils.TrackError(fmt.Errorf("failed to get HCPNodePool from lister: %w", err))
	}
	if existingNodePool.ServiceProviderProperties.DeletionTimestamp != nil {
		return nil
	}

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

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

	return nil
}
