package deploy

import (
	"context"
	"fmt"

	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/google/uuid"
	dplanemgrv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

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

// migrate20260227 initializes the node_instance_groups table from DPM NodePoolConfigs.
// Uses upsert (ON CONFLICT DO NOTHING) to safely handle partial failures and re-runs.
func (m *migrator) migrate20260227(ctx context.Context) gormigrate.MigrateFunc {
	return func(db *gorm.DB) error {
		return InvokeRunMigrateWithRetry(ctx, func() error {
			resp, err := m.dataplaneClient.ListNodePoolConfigs(ctx, &dplanemgrv1.ListNodePoolConfigsRequest{})
			if err != nil {
				return fmt.Errorf("failed to list node pool configs from dataplane manager: %w", err)
			}

			configs := resp.GetNodePoolConfigs()
			if len(configs) == 0 {
				tilog.Info(ctx, "no node pool configs found, nothing to sync")
				return nil
			}

			synced := 0
			for _, cfg := range configs {
				if cfg == nil || cfg.GetInstanceType() == "" || cfg.GetCpuCore() == 0 || cfg.GetMemoryGi() == 0 {
					continue
				}

				cpu := cfg.GetCpuCore()
				memGi := cfg.GetMemoryGi()
				// Use lowercase format (e.g., "8c16g") to match repo layer convention.
				nodeSpec := fmt.Sprintf("%dc%dg", cpu, memGi)

				model := &schema.NodeInstanceGroup{
					ID:                uuid.New().String(),
					NodeSpec:          nodeSpec,
					PrimaryInstance:   cfg.GetInstanceType(),
					CPU:               cpu,
					MemoryGi:          memGi,
					AvailableCPUCore:  cfg.GetAvailableCpuMilliCore() / 1000,
					AvailableMemoryMi: cfg.GetAvailableMemoryMi(),
					WithLocalDisk:     cfg.GetWithLocalDisk(),
					LocalDiskSizeGi:   cfg.GetLocalDiskSizeGi(),
					Arch:              "arm64", // DPM does not return arch yet, default to arm64
					Phase:             schema.NodeInstanceGroupStatusActive,
				}

				// Use Clauses(OnConflict DoNothing) to skip existing records
				result := db.Clauses(clause.OnConflict{DoNothing: true}).Create(model)
				if result.Error != nil {
					return fmt.Errorf("failed to create node instance group %s/%s: %w",
						nodeSpec, cfg.GetInstanceType(), result.Error)
				}

				if result.RowsAffected > 0 {
					synced++
					tilog.Info(ctx, "synced node instance group from node pool config",
						zap.String("nodeSpec", nodeSpec),
						zap.String("instanceType", cfg.GetInstanceType()),
						zap.Uint32("cpu", cpu),
						zap.Uint32("memoryGi", memGi))
				}
			}

			tilog.Info(ctx, "node instance group sync completed",
				zap.Int("configCount", len(configs)),
				zap.Int("synced", synced))
			return nil
		})
	}
}
