package migration

import (
	"errors"
	"fmt"
	"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"
)

// Constants for better maintainability
const (
	// Instance type specs
	C4A_STANDARD_32_CPU    = 32
	C4A_STANDARD_32_MEMORY = 128
	// c4a-highcpu-16
	C4A_HIGHCPU_16_CPU    = 16
	C4A_HIGHCPU_16_MEMORY = 32
	// c4a-highcpu-32
	C4A_HIGHCPU_32_CPU    = 32
	C4A_HIGHCPU_32_MEMORY = 64
	// c4a-highcpu-64
	C4A_HIGHCPU_64_CPU    = 64
	C4A_HIGHCPU_64_MEMORY = 128
)

// Add new gcp c4a instance types and skus
// Created:
// - 4 GCP C4A instance types (c4a-standard-32, c4a-highcpu-16, c4a-highcpu-32, c4a-highcpu-64)
// - 1 storage template (hyperdisk-balanced)
// - 3 TiDB component tiers and SKUs (16C32G, 32C64G, 32C128G)
// - 1 TiKV component tier and 1 SKUs (32C128G with hyperdisk-balanced)
// - 3 PD component tiers and SKUs (16C32G, 32C64G, 64C128G)
// - 1 PD-TiKV mappings for tier compatibility
var _ = AddDBMigrateRecord(
	"202509091600",
	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
			}

			if provider == nil || provider.ID == 0 {
				return errors.New("invalid GCP provider data")
			}

			// Add instance types.
			instanceTypes := []*schema.InstanceType{
				{
					InstanceType: "c4a-standard-32",
					ProviderId:   uint64(provider.ID),
					CPUNum:       C4A_STANDARD_32_CPU,
					MemorySizeGi: C4A_STANDARD_32_MEMORY,
					CpuArch:      "amd64",
				},
				{
					InstanceType: "c4a-highcpu-16",
					ProviderId:   uint64(provider.ID),
					CPUNum:       C4A_HIGHCPU_16_CPU,
					MemorySizeGi: C4A_HIGHCPU_16_MEMORY,
					CpuArch:      "amd64",
				},
				{
					InstanceType: "c4a-highcpu-32",
					ProviderId:   uint64(provider.ID),
					CPUNum:       C4A_HIGHCPU_32_CPU,
					MemorySizeGi: C4A_HIGHCPU_32_MEMORY,
					CpuArch:      "amd64",
				},
				{
					InstanceType: "c4a-highcpu-64",
					ProviderId:   uint64(provider.ID),
					CPUNum:       C4A_HIGHCPU_64_CPU,
					MemorySizeGi: C4A_HIGHCPU_64_MEMORY,
					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 fmt.Errorf("failed to create instance type %s: %w", instanceType.InstanceType, err)
				}
			}

			// Verify all instance types were created and have valid IDs
			for _, instanceType := range instanceTypes {
				if instanceType.ID == 0 {
					return fmt.Errorf("instance type %s was not assigned a valid ID", instanceType.InstanceType)
				}
			}

			instanceTypeMap := lo.SliceToMap(
				instanceTypes,
				func(i *schema.InstanceType) (string, *schema.InstanceType) {
					return i.InstanceType, i
				},
			)

			// Add Hyperdisk Balanced volumes
			var hyperdiskBalancedStorage *schema.StorageTemplate
			{
				hyperdiskBalancedStorage = &schema.StorageTemplate{
					VolumeType: "hyperdisk-balanced",
					ProviderId: uint64(provider.ID),
				}

				hyperdiskBalancedStorage.CreatedAt = timestamp
				hyperdiskBalancedStorage.UpdatedAt = timestamp
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "volume_type"}, {Name: "provider_id"}},
					UpdateAll: true,
				}).Create(hyperdiskBalancedStorage).Error; err != nil {
					return fmt.Errorf("failed to create hyperdisk-balanced storage template: %w", err)
				}

				// Verify storage template was created successfully
				if hyperdiskBalancedStorage.ID == 0 {
					return errors.New("hyperdisk-balanced storage template was not assigned a valid ID")
				}
			}

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

				tidbSpu16c32g := &schema.ComponentTier{
					Key:           "16C32G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIDB",
					Name:          "16C32G",
					ComponentType: "tidb",
					DisplayName:   "16 vCPU, 32 GiB",
					CPUNum:        C4A_HIGHCPU_16_CPU,
					MemorySizeGi:  C4A_HIGHCPU_16_MEMORY,
					Version:       "V1",
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(tidbSpu16c32g).Error; err != nil {
					return err
				}
				tidbSkus = append(tidbSkus, &schema.ComponentTierUnit{
					Key:             "gcp:tidb:c4a-highcpu-16",
					Priority:        DEFAULT_PRIORITY,
					ComponentType:   "tidb",
					ProviderId:      uint64(provider.ID),
					ComponentTierId: uint64(tidbSpu16c32g.ID),
					InstanceTypeId:  uint64(instanceTypeMap["c4a-highcpu-16"].ID),
					DefaultNodeNum:  DEFAULT_TIDB_NODE_NUM,
				})

				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 32c64g tidb skus.
			{
				tidbSkus := make([]*schema.ComponentTierUnit, 0, 4)

				tidbSpu32c64g := &schema.ComponentTier{
					Key:           "32C64G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIDB",
					Name:          "32C64G",
					ComponentType: "tidb",
					DisplayName:   "32 vCPU, 64 GiB",
					CPUNum:        C4A_HIGHCPU_32_CPU,
					MemorySizeGi:  C4A_HIGHCPU_32_MEMORY,
					Version:       "V1",
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(tidbSpu32c64g).Error; err != nil {
					return err
				}
				tidbSkus = append(tidbSkus, &schema.ComponentTierUnit{
					Key:             "gcp:tidb:c4a-highcpu-32",
					Priority:        DEFAULT_PRIORITY,
					ComponentType:   "tidb",
					ProviderId:      uint64(provider.ID),
					ComponentTierId: uint64(tidbSpu32c64g.ID),
					InstanceTypeId:  uint64(instanceTypeMap["c4a-highcpu-32"].ID),
					DefaultNodeNum:  DEFAULT_TIDB_NODE_NUM,
				})
				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 32c128g tidb skus.
			{
				tidbSkus := make([]*schema.ComponentTierUnit, 0, 4)

				tidbSpu32c128g := &schema.ComponentTier{
					Key:           "32C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIDB",
					Name:          "32C128G",
					ComponentType: "tidb",
					DisplayName:   "32 vCPU, 128 GiB",
					CPUNum:        C4A_STANDARD_32_CPU,
					MemorySizeGi:  C4A_STANDARD_32_MEMORY,
					Version:       "V1",
				}
				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:c4a-standard-32",
					Priority:        DEFAULT_PRIORITY,
					ComponentType:   "tidb",
					ProviderId:      uint64(provider.ID),
					ComponentTierId: uint64(tidbSpu32c128g.ID),
					InstanceTypeId:  uint64(instanceTypeMap["c4a-standard-32"].ID),
					DefaultNodeNum:  DEFAULT_TIDB_NODE_NUM,
				})

				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 32c128g tikv skus.
			tikvSku32c128gKey := fmt.Sprintf(
				"gcp:tikv:c4a-standard-32:hyperdisk-balanced:%d:%d",
				HYPERDISK_MAX_IOPS,
				HYPERDISK_MAX_THROUGHPUT,
			)
			{
				tikvSkus := make([]*schema.ComponentTierUnit, 0, 8)

				tikvSpu32c128g := &schema.ComponentTier{
					Key:           "32C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_TIKV",
					Name:          "32C128G",
					ComponentType: "tikv",
					DisplayName:   "32 vCPU, 128 GiB",
					CPUNum:        C4A_STANDARD_32_CPU,
					MemorySizeGi:  C4A_STANDARD_32_MEMORY,
					Version:       "V1",
				}
				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:                    tikvSku32c128gKey,
						Priority:               DEFAULT_PRIORITY,
						ComponentType:          "tikv",
						ProviderId:             uint64(provider.ID),
						ComponentTierId:        uint64(tikvSpu32c128g.ID),
						InstanceTypeId:         uint64(instanceTypeMap["c4a-standard-32"].ID),
						DefaultNodeNum:         DEFAULT_TIKV_NODE_NUM,
						StorageTemplateId:      lo.ToPtr(uint64(hyperdiskBalancedStorage.ID)),
						MinStorageGi:           HYPERDISK_MIN_STORAGE_GI,
						MaxStorageGi:           HYPERDISK_MAX_STORAGE_GI,
						DefaultStorageGi:       HYPERDISK_DEFAULT_STORAGE_GI,
						DefaultIops:            0,
						DefaultThroughPut:      lo.ToPtr(uint32(0)),
						MaxIops:                HYPERDISK_MAX_IOPS,
						MinIops:                HYPERDISK_MIN_IOPS,
						IopsPerGi:              HYPERDISK_IOPS_PER_GI,
						MaxThroughPut:          HYPERDISK_MAX_THROUGHPUT,
						MinThroughPut:          HYPERDISK_MIN_THROUGHPUT,
						ThroughputMibpsPerGi:   HYPERDISK_THROUGHPUT_MIBPS_PER_GI,
						ThroughputMibpsPerIops: HYPERDISK_THROUGHPUT_MIBPS_PER_IOPS,
					})

				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 16c32g pd skus
			var pdSku16c32g *schema.ComponentTierUnit
			{
				pdSpu16c32g := &schema.ComponentTier{
					Key:           "16C32G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_PD",
					Name:          "16C32G",
					ComponentType: "pd",
					DisplayName:   "16 vCPU, 32 GiB",
					CPUNum:        C4A_HIGHCPU_16_CPU,
					MemorySizeGi:  C4A_HIGHCPU_16_MEMORY,
					Version:       "V1",
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSpu16c32g).Error; err != nil {
					return err
				}
				pdSku16c32g = &schema.ComponentTierUnit{
					Key:               fmt.Sprintf("gcp:pd:c4a-highcpu-16:hyperdisk-balanced:%d:%d", PD_DEFAULT_IOPS, PD_DEFAULT_THROUGHPUT),
					Priority:          DEFAULT_PRIORITY,
					ComponentType:     "pd",
					ProviderId:        uint64(provider.ID),
					ComponentTierId:   uint64(pdSpu16c32g.ID),
					InstanceTypeId:    uint64(instanceTypeMap["c4a-highcpu-16"].ID),
					DefaultNodeNum:    DEFAULT_PD_NODE_NUM,
					StorageTemplateId: lo.ToPtr(uint64(hyperdiskBalancedStorage.ID)),
					MinStorageGi:      PD_STORAGE_GI,
					MaxStorageGi:      PD_STORAGE_GI,
					DefaultStorageGi:  PD_STORAGE_GI,
					DefaultIops:       PD_DEFAULT_IOPS,
					DefaultThroughPut: lo.ToPtr(uint32(PD_DEFAULT_THROUGHPUT)),
				}
				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 32c64g pd skus
			var pdSku32c64g *schema.ComponentTierUnit
			{
				pdSpu32c64g := &schema.ComponentTier{
					Key:           "32C64G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_PD",
					Name:          "32C64G",
					ComponentType: "pd",
					DisplayName:   "32 vCPU, 64 GiB",
					CPUNum:        C4A_HIGHCPU_32_CPU,
					MemorySizeGi:  C4A_HIGHCPU_32_MEMORY,
					Version:       "V1",
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSpu32c64g).Error; err != nil {
					return err
				}
				pdSku32c64g = &schema.ComponentTierUnit{
					Key:               fmt.Sprintf("gcp:pd:c4a-highcpu-32:hyperdisk-balanced:%d:%d", PD_DEFAULT_IOPS, PD_DEFAULT_THROUGHPUT),
					Priority:          DEFAULT_PRIORITY,
					ComponentType:     "pd",
					ProviderId:        uint64(provider.ID),
					ComponentTierId:   uint64(pdSpu32c64g.ID),
					InstanceTypeId:    uint64(instanceTypeMap["c4a-highcpu-32"].ID),
					DefaultNodeNum:    DEFAULT_PD_NODE_NUM,
					StorageTemplateId: lo.ToPtr(uint64(hyperdiskBalancedStorage.ID)),
					MinStorageGi:      PD_STORAGE_GI,
					MaxStorageGi:      PD_STORAGE_GI,
					DefaultStorageGi:  PD_STORAGE_GI,
					DefaultIops:       PD_DEFAULT_IOPS,
					DefaultThroughPut: lo.ToPtr(uint32(PD_DEFAULT_THROUGHPUT)),
				}
				pdSku32c64g.CreatedAt = timestamp
				pdSku32c64g.UpdatedAt = timestamp
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSku32c64g).Error; err != nil {
					return err
				}
			}

			// Add 64c128g pd skus
			var pdSku64c128g *schema.ComponentTierUnit
			{
				pdSpu64c128g := &schema.ComponentTier{
					Key:           "64C128G:DEPLOY_TYPE_DEDICATED:COMPONENT_TYPE_PD",
					Name:          "64C128G",
					ComponentType: "pd",
					DisplayName:   "64 vCPU, 128 GiB",
					CPUNum:        C4A_HIGHCPU_64_CPU,
					MemorySizeGi:  C4A_HIGHCPU_64_MEMORY,
					Version:       "V1",
				}
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSpu64c128g).Error; err != nil {
					return err
				}
				pdSku64c128g = &schema.ComponentTierUnit{
					Key:               fmt.Sprintf("gcp:pd:c4a-highcpu-64:hyperdisk-balanced:%d:%d", PD_DEFAULT_IOPS, PD_DEFAULT_THROUGHPUT),
					Priority:          DEFAULT_PRIORITY,
					ComponentType:     "pd",
					ProviderId:        uint64(provider.ID),
					ComponentTierId:   uint64(pdSpu64c128g.ID),
					InstanceTypeId:    uint64(instanceTypeMap["c4a-highcpu-64"].ID),
					DefaultNodeNum:    DEFAULT_PD_NODE_NUM,
					StorageTemplateId: lo.ToPtr(uint64(hyperdiskBalancedStorage.ID)),
					MinStorageGi:      PD_STORAGE_GI,
					MaxStorageGi:      PD_STORAGE_GI,
					DefaultStorageGi:  PD_STORAGE_GI,
					DefaultIops:       PD_DEFAULT_IOPS,
					DefaultThroughPut: lo.ToPtr(uint32(PD_DEFAULT_THROUGHPUT)),
				}
				pdSku64c128g.CreatedAt = timestamp
				pdSku64c128g.UpdatedAt = timestamp
				if err := tx.Clauses(clause.OnConflict{
					Columns:   []clause.Column{{Name: "key"}},
					UpdateAll: true,
				}).Create(pdSku64c128g).Error; err != nil {
					return err
				}
			}

			// Add pd tikv mappings
			pdTikvMappings := []*schema.TierUnitCondition{
				{
					ComponentTierUnitId: uint64(pdSku16c32g.ID), // 16c32g pd for 32c128g tikv
					Key:                 globaldedicatedv1.Sku_Condition_ATTACH_TIERUNIT.String(),
					Operator:            globaldedicatedv1.Sku_Condition_IN.String(),
					Values:              fmt.Sprintf(`[%q]`, tikvSku32c128gKey),
				},
			}

			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
		})
	},
)
