package common

import (
	"context"
	"fmt"
	"time"

	pcav1beta1 "github.com/cert-manager/aws-privateca-issuer/pkg/api/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/helm"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
)

// TODO(tennix): we can remove this when we set the nameOverride in helm values for all environments.
const (
	generatedName     = "aws-pca-issuer-aws-privateca-issuer"
	clusterIssuerName = "cluster-issuer"
)

type PCAIssuer struct {
	*task.BaseTask
}

func NewPCAIssuer(ctx *task.Context) *PCAIssuer {
	return &PCAIssuer{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

func (t *PCAIssuer) Name() string {
	return "PCAIssuer"
}

func (t *PCAIssuer) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	if err := helm.Apply(ctx, t.Remote, &t.InfraTmplConfig.PCAIssuer); err != nil {
		return deploy.Error(fmt.Errorf("failed to apply PCAIssuer helm chart: %w", err))
	}

	caIdentifier, region, enabled, err := t.PCAIdentifierAndRegion()
	if err != nil {
		return deploy.Error(fmt.Errorf("failed to resolve PCA config: %w", err))
	}
	if !enabled {
		return deploy.Done()
	}
	remoteCli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Error(fmt.Errorf("failed to get remote client: %w", err))
	}
	issuer := &pcav1beta1.AWSPCAClusterIssuer{
		ObjectMeta: metav1.ObjectMeta{
			Name: clusterIssuerName,
		},
	}
	if _, err = controllerutil.CreateOrUpdate(ctx, remoteCli, issuer, func() error {
		// Only set Arn and Region if they are not already set.
		// Changing these values would cause different PCA to be used for signing certificates.
		if issuer.Spec.Arn == "" {
			issuer.Spec.Arn = caIdentifier
		}
		if issuer.Spec.Region == "" {
			issuer.Spec.Region = region
		}
		return nil
	}); err != nil {
		return deploy.Error(fmt.Errorf("failed to create or update AWS PCA ClusterIssuer: %w", err))
	}
	return deploy.Done()
}

func (t *PCAIssuer) PostCreate(ctx context.Context) deploy.Result {
	deployName := generatedName
	if override, ok := t.InfraTmplConfig.PCAIssuer.HelmValues["nameOverride"]; ok {
		if name, ok := override.(string); !ok || name == "" {
			return deploy.Error(fmt.Errorf("nameOverride specified but invalid, got: %s (%T)", override, override))
		} else {
			deployName = name
		}
	}
	if err := controllers.EnsureChartDeploymentReady(ctx, t.Remote, types.NamespacedName{
		Namespace: t.InfraTmplConfig.PCAIssuer.GetNamespace(),
		Name:      deployName,
	}); err != nil {
		return deploy.Error(fmt.Errorf("failed to ensure PCAIssuer deployment is ready: %w", err))
	}

	_, _, enabled, err := t.PCAIdentifierAndRegion()
	if err != nil {
		return deploy.Error(fmt.Errorf("failed to resolve PCA config: %w", err))
	}
	if !enabled {
		return deploy.Done()
	}
	remoteCli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Error(fmt.Errorf("failed to get remote client: %w", err))
	}
	var issuer pcav1beta1.AWSPCAClusterIssuer
	if err := remoteCli.Get(ctx, client.ObjectKey{Name: clusterIssuerName}, &issuer); err != nil {
		return deploy.Error(fmt.Errorf("failed to get AWS PCA ClusterIssuer: %w", err))
	}
	for _, v := range issuer.Status.Conditions {
		if v.Type == "Ready" && v.Status == "True" {
			return deploy.Done()
		}
	}
	return deploy.Error(fmt.Errorf("AWS PCA ClusterIssuer is not ready yet"))
}
