// Copyright (c) 2026 Sidero Labs, Inc.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.

package omni

import (
	"context"

	"github.com/cosi-project/runtime/pkg/controller"
	"github.com/cosi-project/runtime/pkg/controller/generic/cleanup"
	"github.com/cosi-project/runtime/pkg/resource"
	"github.com/cosi-project/runtime/pkg/state"
	"go.uber.org/zap"
	"k8s.io/client-go/rest"

	"github.com/siderolabs/omni/client/api/common"
	"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
	"github.com/siderolabs/omni/internal/backend/runtime/kubernetes"
	customcleanup "github.com/siderolabs/omni/internal/backend/runtime/omni/controllers/cleanup"
)

// ClusterController manages Cluster resource lifecycle.
//
// ClusterController plays the role of machine discovery.
type ClusterController = cleanup.Controller[*omni.Cluster]

const clusterControllerName = "ClusterController"

type handler struct {
	handler           cleanup.Handler[*omni.Cluster]
	kubernetesRuntime KubernetesRuntime
	finalizer         string
}

func (c handler) FinalizerRemoval(ctx context.Context, r controller.Runtime, logger *zap.Logger, input *omni.Cluster) error {
	if err := c.handler.FinalizerRemoval(ctx, r, logger, input); err != nil {
		return err
	}

	// destroy client cache for the destroyed cluster
	c.kubernetesRuntime.DestroyClient(input.Metadata().ID())

	return nil
}

func (c handler) Inputs() []controller.Input {
	return c.handler.Inputs()
}

func (c handler) Outputs() []controller.Output {
	return c.handler.Outputs()
}

// KubernetesRuntime defines the interface for interacting with the Kubernetes runtime.
type KubernetesRuntime interface {
	DestroyClient(string)
	GetClient(ctx context.Context, cluster string) (*kubernetes.Client, error)
	GetKubeconfig(ctx context.Context, context *common.Context) (*rest.Config, error)
	StartCacheManager(ctx context.Context) error
}

// NewClusterController creates new ClusterController.
func NewClusterController(kubernetesRuntime KubernetesRuntime) *ClusterController {
	return cleanup.NewController(
		cleanup.Settings[*omni.Cluster]{
			Name: clusterControllerName,
			Handler: handler{
				kubernetesRuntime: kubernetesRuntime,
				finalizer:         clusterControllerName,
				handler: cleanup.Combine(
					cleanup.RemoveOutputs[*omni.MachineSet](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(
							resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()),
							resource.LabelExists(omni.LabelWorkerRole),
						)
					}),
					cleanup.RemoveOutputs[*omni.MachineSet](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(
							resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()),
							resource.LabelExists(omni.LabelControlPlaneRole),
						)
					}),
					cleanup.RemoveOutputs[*omni.ConfigPatch](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					cleanup.RemoveOutputs[*omni.KubernetesManifestGroup](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					cleanup.RemoveOutputs[*omni.ExtensionsConfiguration](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					cleanup.HasNoOutputs[*omni.ClusterMachine](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					cleanup.HasNoOutputs[*omni.ClusterMachineConfigPatches](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					cleanup.HasNoOutputs[*omni.ClusterMachineConfig](func(cluster *omni.Cluster) state.ListOption {
						return state.WithLabelQuery(resource.LabelEqual(omni.LabelCluster, cluster.Metadata().ID()))
					}),
					&customcleanup.SameIDHandler[*omni.Cluster, *omni.ImportedClusterSecrets]{},
					&customcleanup.SameIDHandler[*omni.Cluster, *omni.RotateTalosCA]{},
					&customcleanup.SameIDHandler[*omni.Cluster, *omni.RotateKubernetesCA]{},
				),
			},
		},
	)
}
