package migration

import (
	"time"

	"github.com/samber/lo"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/infra/repoimpl/schema"
)

// Batch add gcp 32c SKUs
var _ = AddDBMigrateRecord(
	"202507012200",
	func(db *gorm.DB) error {
		timestamp := time.Now()

		return db.Transaction(func(tx *gorm.DB) error {
			// Fetch the GCP provider.
			var provider *schema.Provider
			if err := tx.Where("name = ?", "gcp").First(&provider).Error; err != nil {
				return err
			}

			// Add instance types.
			instanceTypes := []*schema.InstanceType{
				{
					InstanceType: "n2-standard-32",
					ProviderId:   uint64(provider.ID),
					CPUNum:       32,
					MemorySizeGi: 128,
					CpuArch:      "amd64",
				},
				{
					InstanceType: "n2-custom-8-16384",
					ProviderId:   uint64(provider.ID),
					CPUNum:       8,
					MemorySizeGi: 16,
					CpuArch:      "amd64",
				},
				{
					InstanceType: "n2-custom-16-32768",
					ProviderId:   uint64(provider.ID),
					CPUNum:       16,
					MemorySizeGi: 32,
					CpuArch:      "amd64",
				},
			}
			for _, instanceType := range instanceTypes {
				instanceType.CreatedAt = timestamp
				instanceType.UpdatedAt = timestamp
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "instance_type"}},
					UpdateAll: true,
				}).Create(instanceType).Error; err != nil {
					return err
				}
			}
			instanceTypeMap := lo.SliceToMap(
				instanceTypes,
				func(i *schema.InstanceType) (string, *schema.InstanceType) {
					return i.InstanceType, i
				},
			)

			// Fetch storage for pd-ssd volumes.
			var pdssdStorage *schema.StorageTemplate
			if err := db.Where("volume_type = ?", "pd-ssd").First(&pdssdStorage).Error; err != nil {
				return err
			}

			// Add tidb skus.
			{
				tidbSkus := make([]*schema.ComponentTierUnit, 0, 4)

				tidbSpu32c128g := &schema.ComponentTier{
					Key:           "32C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIDB",
					ComponentType: "tidb",
					DisplayName:   "32 vCPU, 128 GiB",
					CPUNum:        32,
					MemorySizeGi:  128,
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(tidbSpu32c128g).Error; err != nil {
					return err
				}
				tidbSkus = append(tidbSkus, &schema.ComponentTierUnit{
					Key:             "gcp:tidb:n2-standard-32",
					Priority:        999,
					ComponentType:   "tidb",
					ProviderId:      uint64(provider.ID),
					ComponentTierId: uint64(tidbSpu32c128g.ID),
					InstanceTypeId:  uint64(instanceTypeMap["n2-standard-32"].ID),
					DefaultNodeNum:  2,
				})

				for _, tidbSku := range tidbSkus {
					tidbSku.CreatedAt = timestamp
					tidbSku.UpdatedAt = timestamp
					if err := tx.Clauses(clause.OnConflict{
						Columns:   []clause.Column{{Name: "key"}},
						UpdateAll: true,
					}).Create(tidbSku).Error; err != nil {
						return err
					}
				}
			}

			// Add tikv skus.
			{
				tikvSkus := make([]*schema.ComponentTierUnit, 0, 8)

				tikvSpu32c128g := &schema.ComponentTier{
					Key:           "32C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIKV",
					ComponentType: "tikv",
					DisplayName:   "32 vCPU, 128 GiB",
					CPUNum:        32,
					MemorySizeGi:  128,
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(tikvSpu32c128g).Error; err != nil {
					return err
				}
				tikvSkus = append(tikvSkus,
					&schema.ComponentTierUnit{
						Key:               "gcp:tikv:n2-standard-32:pd-ssd:0:0",
						Priority:          999,
						ComponentType:     "tikv",
						ProviderId:        uint64(provider.ID),
						ComponentTierId:   uint64(tikvSpu32c128g.ID),
						InstanceTypeId:    uint64(instanceTypeMap["n2-standard-32"].ID),
						DefaultNodeNum:    3,
						StorageTemplateId: lo.ToPtr(uint64(pdssdStorage.ID)),
						MinStorageGi:      200,
						MaxStorageGi:      4096,
						DefaultStorageGi:  500,
					})

				for _, tikvSku := range tikvSkus {
					tikvSku.CreatedAt = timestamp
					tikvSku.UpdatedAt = timestamp
					if err := tx.Clauses(clause.OnConflict{
						Columns:   []clause.Column{{Name: "key"}},
						UpdateAll: true,
					}).Create(tikvSku).Error; err != nil {
						return err
					}
				}
			}

			// Add tiflash skus.
			{
				tiflashSkus := make([]*schema.ComponentTierUnit, 0, 8)

				tiflashSpu32c128g := &schema.ComponentTier{
					Key:           "32C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIFLASH",
					ComponentType: "tiflash",
					DisplayName:   "32 vCPU, 128 GiB",
					CPUNum:        32,
					MemorySizeGi:  128,
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(tiflashSpu32c128g).Error; err != nil {
					return err
				}
				tiflashSkus = append(tiflashSkus,
					&schema.ComponentTierUnit{
						Key:               "gcp:tiflash:n2-standard-32:pd-ssd:0:0",
						Priority:          999,
						ComponentType:     "tiflash",
						ProviderId:        uint64(provider.ID),
						ComponentTierId:   uint64(tiflashSpu32c128g.ID),
						InstanceTypeId:    uint64(instanceTypeMap["n2-standard-32"].ID),
						DefaultNodeNum:    0,
						StorageTemplateId: lo.ToPtr(uint64(pdssdStorage.ID)),
						MinStorageGi:      200,
						MaxStorageGi:      6144,
						DefaultStorageGi:  500,
					})

				for _, tiflashSku := range tiflashSkus {
					tiflashSku.CreatedAt = timestamp
					tiflashSku.UpdatedAt = timestamp
					if err := tx.Clauses(clause.OnConflict{
						Columns:   []clause.Column{{Name: "key"}},
						UpdateAll: true,
					}).Create(tiflashSku).Error; err != nil {
						return err
					}
				}
			}

			// Add pd skus
			var pdSku16c32g *schema.ComponentTierUnit
			{
				pdSpu16c32g := &schema.ComponentTier{
					Key:           "16C32G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_PD",
					ComponentType: "pd",
					DisplayName:   "16 vCPU, 32 GiB",
					CPUNum:        16,
					MemorySizeGi:  32,
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSpu16c32g).Error; err != nil {
					return err
				}
				pdSku16c32g = &schema.ComponentTierUnit{
					Key:               "gcp:pd:n2-custom-16-32768:pd-ssd:0:0",
					Priority:          999,
					ComponentType:     "pd",
					ProviderId:        uint64(provider.ID),
					ComponentTierId:   uint64(pdSpu16c32g.ID),
					InstanceTypeId:    uint64(instanceTypeMap["n2-custom-16-32768"].ID),
					DefaultNodeNum:    3,
					StorageTemplateId: lo.ToPtr(uint64(pdssdStorage.ID)),
					MinStorageGi:      20,
					MaxStorageGi:      20,
					DefaultStorageGi:  20,
				}
				pdSku16c32g.CreatedAt = timestamp
				pdSku16c32g.UpdatedAt = timestamp

				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSku16c32g).Error; err != nil {
					return err
				}
			}

			// Add pd tikv mappings
			pdTikvMappings := []*schema.TierUnitCondition{
				{
					ComponentTierUnitId: uint64(pdSku16c32g.ID),
					Key:                 globaldedicatedv1.Sku_Condition_ATTACH_TIERUNIT.String(),
					Operator:            globaldedicatedv1.Sku_Condition_IN.String(),
					Values: `[
"gcp:tikv:n2-standard-32:pd-ssd:0:0"
]`,
				},
			}

			for _, pdTikvMapping := range pdTikvMappings {
				pdTikvMapping.CreatedAt = timestamp
				pdTikvMapping.UpdatedAt = timestamp
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}, {Name: "component_tier_unit_id"}},
					UpdateAll: true,
				}).Create(pdTikvMapping).Error; err != nil {
					return err
				}
			}

			return nil
		})
	},
)
