package deploy

import (
	"context"
	"fmt"
	"strconv"

	"github.com/blang/semver/v4"
	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/pingcap/tidb-operator/api/v2/core/v1alpha1"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	tiProxyGroupCRDName           = "tiproxygroups.core.pingcap.com"
	tidbOperatorVersionAnnotation = "pingcap.com/version"
)

var (
	tiProxyGroupCRDGKV = schema.GroupVersionKind{
		Group:   "apiextensions.k8s.io",
		Version: "v1",
		Kind:    "CustomResourceDefinition",
	}
	minSafeTiDBOperatorVersion = semver.MustParse("2.1.0-beta.5")
)

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

func (m *migrator) migrate20260604(ctx context.Context) gormigrate.MigrateFunc {
	return func(db *gorm.DB) error {
		return InvokeRunMigrateWithRetry(ctx, func() error {
			return backfillTiProxyGracefulRestartAnnotationsForSharedPools(ctx, m)
		})
	}
}

func backfillTiProxyGracefulRestartAnnotationsForSharedPools(ctx context.Context, m *migrator) error {
	sharedPools, err := m.sharedPoolRepo.List(ctx, &repo.SharedPoolFilter{Deleted: false})
	if err != nil {
		return fmt.Errorf("list shared pools: %w", err)
	}

	totalUpdated := 0
	for _, sharedPool := range sharedPools {
		remoteCli, err := m.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
		if err != nil {
			return fmt.Errorf("get dataplane client %s: %w", sharedPool.GetDataplaneName(), err)
		}

		safe, version, err := tidbOperatorCanPatchTiProxyGracefulRestartAnnotations(ctx, remoteCli)
		if err != nil {
			tilog.Warn(ctx, "skip backfilling TiProxyGroup graceful restart annotations because TiDB operator version cannot be verified",
				zap.String("sharedPoolId", sharedPool.GetId()),
				zap.String("dataplaneName", sharedPool.GetDataplaneName()),
				zap.Error(err),
			)
			continue
		}
		if !safe {
			tilog.Warn(ctx, "skip backfilling TiProxyGroup graceful restart annotations because TiDB operator version may roll TiProxy pods",
				zap.String("sharedPoolId", sharedPool.GetId()),
				zap.String("dataplaneName", sharedPool.GetDataplaneName()),
				zap.String("tidbOperatorVersion", version),
				zap.String("minSafeTiDBOperatorVersion", minSafeTiDBOperatorVersion.String()),
			)
			continue
		}

		updated, err := backfillTiProxyGracefulRestartAnnotationsInNamespace(ctx, remoteCli, sharedPool.GetDataPlaneNamespace())
		if err != nil {
			return fmt.Errorf("backfill TiProxyGroup graceful restart annotations in namespace %s: %w", sharedPool.GetDataPlaneNamespace(), err)
		}
		totalUpdated += updated
	}

	tilog.PlainInfo("backfilled TiProxyGroup graceful restart annotations",
		zap.Int("sharedPoolCount", len(sharedPools)),
		zap.Int("updatedTiProxyGroupCount", totalUpdated),
	)
	return nil
}

func tidbOperatorCanPatchTiProxyGracefulRestartAnnotations(ctx context.Context, remoteCli client.Client) (bool, string, error) {
	crd := &unstructured.Unstructured{}
	crd.SetGroupVersionKind(tiProxyGroupCRDGKV)
	if err := remoteCli.Get(ctx, types.NamespacedName{Name: tiProxyGroupCRDName}, crd); err != nil {
		return false, "", fmt.Errorf("get TiProxyGroup CRD: %w", err)
	}

	version := crd.GetAnnotations()[tidbOperatorVersionAnnotation]
	if version == "" {
		return false, "", fmt.Errorf("TiProxyGroup CRD %s does not have annotation %s", tiProxyGroupCRDName, tidbOperatorVersionAnnotation)
	}

	parsed, err := semver.ParseTolerant(version)
	if err != nil {
		return false, version, fmt.Errorf("parse TiDB operator version %q: %w", version, err)
	}

	return parsed.GTE(minSafeTiDBOperatorVersion), version, nil
}

func backfillTiProxyGracefulRestartAnnotationsInNamespace(ctx context.Context, remoteCli client.Client, namespace string) (int, error) {
	var tiproxyGroups v1alpha1.TiProxyGroupList
	if err := remoteCli.List(ctx, &tiproxyGroups, client.InNamespace(namespace)); err != nil {
		return 0, fmt.Errorf("list TiProxyGroups: %w", err)
	}

	expectedDelay := strconv.FormatInt(entity.DefaultTiProxyGracefulShutdownDeleteDelaySeconds, 10)
	updated := 0
	for i := range tiproxyGroups.Items {
		tiproxyGroup := &tiproxyGroups.Items[i]
		if tiproxyGroup.Spec.Template.Annotations[entity.TiProxyGracefulShutdownDeleteDelaySecondsAnnotation] == expectedDelay {
			continue
		}

		patch := tiproxyGroup.DeepCopy()
		if patch.Spec.Template.Annotations == nil {
			patch.Spec.Template.Annotations = map[string]string{}
		}
		patch.Spec.Template.Annotations[entity.TiProxyGracefulShutdownDeleteDelaySecondsAnnotation] = expectedDelay
		if err := remoteCli.Patch(ctx, patch, client.MergeFrom(tiproxyGroup)); err != nil {
			return updated, fmt.Errorf("patch TiProxyGroup %s/%s: %w", tiproxyGroup.Namespace, tiproxyGroup.Name, err)
		}
		updated++
	}

	return updated, nil
}
