package commontasks

import (
	"context"
	"fmt"

	"sigs.k8s.io/controller-runtime/pkg/client"

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/pkg/consts"
	"github.com/tidbcloud/observability/agent-control-server/pkg/deploy"
	"github.com/tidbcloud/observability/agent-control-server/pkg/o11y"
	"github.com/tidbcloud/observability/agent-control-server/pkg/reconciler"
)

// BackendConfig encapsulates configurable parameters for backend tasks
type BackendConfig struct {
	AlertTemplateID    string
	AlertTemplateIDDev string
	Account            string
	SkipBackendDNSName bool
	WithAgentAccessARN bool
	RequirePeering     bool
}

// DataPlaneBackendConfig returns the default configuration for data plane agents
func DataPlaneBackendConfig() BackendConfig {
	return BackendConfig{
		AlertTemplateID:    "tidb-cloud-data-plane-nextgen-rule",
		AlertTemplateIDDev: "tidb-cloud-data-plane-nextgen-rule-dev",
		Account:            "",
		SkipBackendDNSName: true,
	}
}

// ControlPlaneBackendConfig returns the configuration for control plane agents
func ControlPlaneBackendConfig(account string) BackendConfig {
	return BackendConfig{
		AlertTemplateID:    "tidb-cloud-control-plane-nextgen-k8s-infra-rule",
		AlertTemplateIDDev: "tidb-cloud-control-plane-nextgen-k8s-infra-rule-dev",
		Account:            account,
		SkipBackendDNSName: false,
	}
}

type BaseBackendTask[T client.Object] struct {
	*reconciler.BaseTask[T]
	Inner   *BackendTask
	cm      deploy.ClusterMeta
	o11yV2  agentv2.O11yConfigV2
	bizType string
	config  BackendConfig
}

func NewBaseBackendTask[T client.Object](ctx *reconciler.Context[T], cm deploy.ClusterMeta, o11yConfig agentv2.O11yConfigV2, bizType string, config BackendConfig) *BaseBackendTask[T] {
	return &BaseBackendTask[T]{
		BaseTask: &reconciler.BaseTask[T]{Context: ctx},
		Inner: &BackendTask{
			L:                  ctx.L,
			Agent:              ctx.Agent,
			O11yAPI:            &ctx.O11yAPI,
			PeeringNLB:         ctx.Config.O11yConfig.PeeringNLB,
			WithAgentAccessARN: config.WithAgentAccessARN,
		},
		cm:      cm,
		o11yV2:  o11yConfig,
		bizType: bizType,
		config:  config,
	}
}

func (t *BaseBackendTask[T]) Name() string {
	return "Backend"
}

func (t *BaseBackendTask[T]) ClusterMeta() deploy.ClusterMeta {
	return t.cm
}

func (t *BaseBackendTask[T]) Create(ctx context.Context) deploy.Result {
	alertID := t.config.AlertTemplateID
	if t.Config.O11yConfig.Env != "prod" {
		alertID = t.config.AlertTemplateIDDev
	}
	bizType := t.bizType
	if t.Config.O11yConfig.IsByoc {
		bizType = consts.O11yBizTypeBYOC
	}
	projectID := ""
	if s := t.Agent.GetLabels()[consts.LabelKeyO11yProjectID]; s != "" {
		projectID = s
	}

	params := o11y.CreateParamsNextGen(ctx,
		t.Config.O11yConfig.Vendor,
		t.Config.O11yConfig.LogicRegion,
		bizType,
		t.o11yV2.TenantID,
		t.o11yV2.ApplicationID,
		projectID,
		t.config.Account,
		alertID,
		t.config.SkipBackendDNSName,
	).WithAgentAccessARN(t.Inner.getAgentAccessARN())

	if t.config.RequirePeering &&
		t.Config.O11yConfig.IsByoc &&
		t.Config.O11yConfig.Vendor == consts.VendorTypeAWS &&
		t.Config.O11yConfig.PeeringNLB != "" {
		peeringID := t.Agent.GetAnnotations()[consts.AnnotationKeyBYOCVPCPeeringID]
		dataplaneVPCCIDR := t.Agent.GetAnnotations()[consts.AnnotationKeyBYOCDataplaneVPCCIDR]
		if peeringID == "" || dataplaneVPCCIDR == "" {
			return deploy.Error(fmt.Errorf("waiting for BYOC VPC peering annotations %s and %s", consts.AnnotationKeyBYOCVPCPeeringID, consts.AnnotationKeyBYOCDataplaneVPCCIDR))
		}
		params = params.WithBYOCVPCPeering(peeringID, dataplaneVPCCIDR)
	}

	return t.Inner.Create(ctx, params, false, true)
}

func (t *BaseBackendTask[T]) Delete(ctx context.Context) deploy.Result {
	if t.Agent.GetLabels()[consts.LabelKeyPreserveO11yCluster] == "true" {
		return deploy.Done()
	}

	bizType := t.bizType
	if t.Config.O11yConfig.IsByoc {
		bizType = consts.O11yBizTypeBYOC
	}
	projectID := ""
	if s := t.Agent.GetLabels()[consts.LabelKeyO11yProjectID]; s != "" {
		projectID = s
	}

	return t.Inner.Delete(ctx,
		t.Config.O11yConfig.Vendor,
		t.Config.O11yConfig.LogicRegion,
		t.o11yV2.TenantID,
		projectID,
		t.o11yV2.ApplicationID,
		bizType,
	)
}
