package controllers

import (
	"context"

	cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
	"github.com/go-logr/logr"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
)

const (
	issuerGroup = "alicloudpca.cert-manager.io"
)

// CertificateRequestApprover automatically approves CertificateRequests
// that reference AliCloud PCA issuers.
type CertificateRequestApprover struct {
	client.Client
	Log    logr.Logger
	Scheme *runtime.Scheme
}

// Reconcile approves CertificateRequests that reference AliCloud PCA issuers
func (r *CertificateRequestApprover) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := r.Log.WithValues("certificaterequest", req.NamespacedName)

	// Fetch the CertificateRequest
	var cr cmapi.CertificateRequest
	if err := r.Get(ctx, req.NamespacedName, &cr); err != nil {
		if apierrors.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		log.Error(err, "failed to get CertificateRequest")
		return ctrl.Result{}, err
	}

	// Check if it's for our issuer
	if cr.Spec.IssuerRef.Group != issuerGroup {
		// Not for us, ignore
		return ctrl.Result{}, nil
	}

	// Check if already approved
	if isApproved(&cr) {
		log.V(1).Info("CertificateRequest already approved")
		return ctrl.Result{}, nil
	}

	// Check if already denied
	if isDenied(&cr) {
		log.V(1).Info("CertificateRequest already denied")
		return ctrl.Result{}, nil
	}

	// Approve the CertificateRequest
	log.Info("Auto-approving CertificateRequest",
		"issuer", cr.Spec.IssuerRef.Name,
		"kind", cr.Spec.IssuerRef.Kind)

	cr.Status.Conditions = append(cr.Status.Conditions, cmapi.CertificateRequestCondition{
		Type:               cmapi.CertificateRequestConditionApproved,
		Status:             cmmeta.ConditionTrue,
		Reason:             "AutoApproved",
		Message:            "Certificate request has been automatically approved by alicloud-pca-issuer",
		LastTransitionTime: &metav1.Time{Time: metav1.Now().Time},
	})

	if err := r.Status().Update(ctx, &cr); err != nil {
		log.Error(err, "failed to approve CertificateRequest")
		return ctrl.Result{}, err
	}

	log.Info("Successfully approved CertificateRequest")
	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *CertificateRequestApprover) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		Named("alicloudpca-certificaterequest-approver").
		For(&cmapi.CertificateRequest{}).
		WithEventFilter(predicate.NewPredicateFuncs(func(object client.Object) bool {
			// Only process CertificateRequests for our issuer group
			cr, ok := object.(*cmapi.CertificateRequest)
			if !ok {
				return false
			}
			return cr.Spec.IssuerRef.Group == issuerGroup
		})).
		Complete(r)
}

// isApproved checks if the CertificateRequest has been approved
func isApproved(cr *cmapi.CertificateRequest) bool {
	for _, condition := range cr.Status.Conditions {
		if condition.Type == cmapi.CertificateRequestConditionApproved &&
			condition.Status == cmmeta.ConditionTrue {
			return true
		}
	}
	return false
}

// isDenied checks if the CertificateRequest has been denied
func isDenied(cr *cmapi.CertificateRequest) bool {
	for _, condition := range cr.Status.Conditions {
		if condition.Type == cmapi.CertificateRequestConditionDenied &&
			condition.Status == cmmeta.ConditionTrue {
			return true
		}
	}
	return false
}
