package deploy

import (
	"context"
	"fmt"

	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	o11yv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func init() {
	registerMigrateRecord((*migrator).migrate20260707)
}

func (m *migrator) migrate20260707(ctx context.Context) gormigrate.MigrateFunc {
	return func(db *gorm.DB) error {
		return InvokeRunMigrateWithRetry(ctx, func() error {
			if m.kubeClient == nil {
				return fmt.Errorf("kube client is nil")
			}
			updated, err := backfillO11yTiDBGroupAgentLabels(ctx, m.kubeClient)
			if err != nil {
				return err
			}
			tilog.Info(ctx, "backfilled o11y tidbgroup agent labels", zap.Int("updatedTiDBGroupAgentCount", updated))
			return nil
		})
	}
}

func backfillO11yTiDBGroupAgentLabels(ctx context.Context, kubeClient client.Client) (int, error) {
	var agents o11yv2.TiDBGroupAgentList
	if err := kubeClient.List(ctx, &agents, client.InNamespace(entity.GetNamespace())); err != nil {
		return 0, fmt.Errorf("list o11y tidbgroup agents: %w", err)
	}

	updated := 0
	for i := range agents.Items {
		agent := &agents.Items[i]
		original := agent.DeepCopy()
		if agent.Labels == nil {
			agent.Labels = map[string]string{}
		}

		changed := false
		if agent.Labels[entity.O11yTiDBGroupAgentClusterIDLabelKey] == "" && agent.Spec.ClusterID != "" {
			agent.Labels[entity.O11yTiDBGroupAgentClusterIDLabelKey] = agent.Spec.ClusterID
			changed = true
		}
		if agent.Labels[entity.O11yTiDBGroupAgentGroupIDLabelKey] == "" && agent.Spec.TiDBGroup != "" {
			agent.Labels[entity.O11yTiDBGroupAgentGroupIDLabelKey] = agent.Spec.TiDBGroup
			changed = true
		}
		if !changed {
			continue
		}

		if err := kubeClient.Patch(ctx, agent, client.MergeFrom(original)); err != nil {
			return updated, fmt.Errorf("patch o11y tidbgroup agent %s/%s labels: %w", agent.Namespace, agent.Name, err)
		}
		updated++
	}

	return updated, nil
}
