package common

import (
	"context"
	"errors"
	"fmt"
	"path"
	"time"

	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/types"

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

	"helm.sh/helm/v3/pkg/chart"
	"helm.sh/helm/v3/pkg/chartutil"
	"helm.sh/helm/v3/pkg/engine"

	"sigs.k8s.io/yaml"
)

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())
	return deploy.ErrorOrDone(helm.Apply(ctx, t.Remote, &t.InfraTmplConfig.PCAIssuer))
}

func (t *PCAIssuer) PostCreate(ctx context.Context) deploy.Result {
	rendered, err := renderPCAIssuerDeployment(&t.InfraTmplConfig.PCAIssuer)
	if err != nil {
		return deploy.Error(err)
	}
	return deploy.ErrorOrDone(controllers.EnsureChartDeploymentReady(ctx, t.Remote, types.NamespacedName{
		Namespace: t.InfraTmplConfig.PCAIssuer.GetNamespace(),
		Name:      rendered.Name,
	}))
}

/*
aws-privateca-issuer-1.2.0-fixed.tgz
./crds/
./Chart.yaml
./.helmignore
./templates/
./values.yaml
./templates/service-monitor.yaml
./templates/deployment.yaml
./templates/service.yaml
./templates/hpa.yaml
./templates/rbac.yaml
./templates/_helpers.tpl
./crds/awspca.cert-manager.io_awspcaclusterissuers.yaml
./crds/awspca.cert-manager.io_awspcaissuers.yaml
*/

func renderPCAIssuerDeployment(cfg *rsconfig.InfraComponentConfig) (*appsv1.Deployment, error) {
	key := ""
	chrt, err := cfg.ToHelmChart()
	if err != nil {
		return nil, err
	}
	tpls := make([]*chart.File, 0)
	for _, tpl := range chrt.Templates {
		base := path.Base(tpl.Name)
		if base == "deployment.yaml" {
			tpls = append(tpls, tpl)
			key = path.Join(chrt.Metadata.Name, tpl.Name)
		}
		if base == "_helpers.tpl" {
			tpls = append(tpls, tpl)
		}
	}
	if key == "" {
		return nil, errors.New("deployment.yaml not found in helm package")
	}
	chrt.Templates = tpls
	coalesceValues, err := chartutil.CoalesceValues(chrt, cfg.ToHelmValues())
	if err != nil {
		return nil, err
	}
	helmValues := map[string]interface{}{
		"Values": coalesceValues,
		"Release": map[string]interface{}{
			"Name":      cfg.ReleaseName,
			"Namespace": cfg.Namespace,
		},
	}
	out, err := engine.Render(chrt, helmValues)
	if err != nil {
		return nil, err
	}
	data := out[key]
	if data == "" {
		return nil, fmt.Errorf("render helm chart template get empty result for %s", cfg.ChartPath)
	}
	cr := new(appsv1.Deployment)
	if err := yaml.Unmarshal([]byte(data), cr); err != nil {
		return nil, err
	}
	return cr, nil
}
