package harvester

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/log"

	"vanderlande.com/ittp/appstack/rig-operator/api/v1alpha1"
)

type IdentityManager struct {
	client client.Client
	scheme *runtime.Scheme
}

func NewIdentityManager(c client.Client, s *runtime.Scheme) *IdentityManager {
	return &IdentityManager{client: c, scheme: s}
}

// Ensure checks if an identity exists. If not, it fetches master creds, mints a new one, and updates Status.
func (m *IdentityManager) Ensure(ctx context.Context, cbp *v1alpha1.ClusterBlueprint, ibp *v1alpha1.InfraBlueprint, hbp *v1alpha1.HarvesterBlueprint) (string, error) {
	l := log.FromContext(ctx)

	// 1. Fast Path: If identity already exists in Status, return it
	if cbp.Status.Identity != nil && cbp.Status.Identity.SecretRef != "" {
		return cbp.Status.Identity.SecretRef, nil
	}

	l.Info("Minting Harvester identity", "Cluster", cbp.Name)

	// 2. Fetch Master Credential (from Infra)
	rancherCredName := ibp.Spec.CloudCredentialSecret
	if rancherCredName == "" {
		return "", fmt.Errorf("CloudCredentialSecret is missing in InfraBlueprint %s", ibp.Name)
	}

	var rancherSecret corev1.Secret
	// Note: Rancher secrets are expected in cattle-global-data
	if err := m.client.Get(ctx, types.NamespacedName{Name: rancherCredName, Namespace: "cattle-global-data"}, &rancherSecret); err != nil {
		return "", fmt.Errorf("failed to fetch rancher credential %s: %w", rancherCredName, err)
	}

	// 3. Extract Kubeconfig
	const kubeconfigKey = "harvestercredentialConfig-kubeconfigContent"
	adminKubeconfigBytes := rancherSecret.Data[kubeconfigKey]
	if len(adminKubeconfigBytes) == 0 {
		if len(rancherSecret.Data["credential"]) > 0 {
			adminKubeconfigBytes = rancherSecret.Data["credential"]
		} else {
			return "", fmt.Errorf("secret %s missing kubeconfig data", rancherCredName)
		}
	}

	// 4. Call Factory (low-level)
	newSecret, saName, _, err := EnsureCredential(
		ctx,
		adminKubeconfigBytes,
		cbp.Name,
		cbp.Namespace,         // Target Namespace (where secret goes)
		hbp.Spec.VmNamespace,  // Harvester Namespace (where VM goes)
		hbp.Spec.HarvesterURL, // Explicit URL from HBP
	)
	if err != nil {
		return "", fmt.Errorf("failed to mint harvester credential: %w", err)
	}

	// 5. Persist Secret
	// Set OwnerRef so if CBP is deleted, Secret is deleted automatically
	if err := controllerutil.SetControllerReference(cbp, newSecret, m.scheme); err != nil {
		return "", err
	}

	patchOpts := []client.PatchOption{client.ForceOwnership, client.FieldOwner("rig-operator")}
	if err := m.client.Patch(ctx, newSecret, client.Apply, patchOpts...); err != nil {
		return "", fmt.Errorf("failed to patch new secret: %w", err)
	}

	// 6. Update CBP Status
	// We do this here so the identity is "locked" to the object immediately
	if cbp.Status.Identity == nil {
		cbp.Status.Identity = &v1alpha1.IdentityStatus{}
	}
	cbp.Status.Identity.SecretRef = newSecret.Name
	cbp.Status.Identity.ServiceAccount = saName

	if err := m.client.Status().Update(ctx, cbp); err != nil {
		return "", fmt.Errorf("failed to update cluster status: %w", err)
	}

	return newSecret.Name, nil
}

// Cleanup removes the ServiceAccount from Harvester when the Cluster is deleted
func (m *IdentityManager) Cleanup(ctx context.Context, cbp *v1alpha1.ClusterBlueprint, ibp *v1alpha1.InfraBlueprint, hbp *v1alpha1.HarvesterBlueprint) {
	if cbp.Status.Identity == nil || cbp.Status.Identity.ServiceAccount == "" {
		return
	}

	// Fetch Master Secret again to get connection details
	rancherCredName := ibp.Spec.CloudCredentialSecret
	var rancherSecret corev1.Secret
	if err := m.client.Get(ctx, types.NamespacedName{Name: rancherCredName, Namespace: "cattle-global-data"}, &rancherSecret); err != nil {
		log.FromContext(ctx).V(1).Info("Cleanup: Could not fetch master secret (connection lost), skipping manual cleanup")
		return
	}

	var kubeBytes []byte
	if len(rancherSecret.Data["harvestercredentialConfig-kubeconfigContent"]) > 0 {
		kubeBytes = rancherSecret.Data["harvestercredentialConfig-kubeconfigContent"]
	} else if len(rancherSecret.Data["credential"]) > 0 {
		kubeBytes = rancherSecret.Data["credential"]
	} else {
		return
	}

	// Delegate to low-level cleanup
	if err := DeleteCredentialResources(ctx, kubeBytes, cbp.Status.Identity.ServiceAccount, hbp.Spec.VmNamespace); err != nil {
		log.FromContext(ctx).Error(err, "Failed to cleanup Harvester resources (best effort)")
	}
}
