package repository

import (
	"context"

	"github.com/tidbcloud/pkgv2/db"
	"gorm.io/gorm"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	enumutil "github.com/tidbcloud/tidb-management-service/utilities/enum"
)

type MigrationClusterRepoImpl struct {
	dbProvider db.Provider
}

func NewMigrationClusterRepoRepo(dbProvider db.Provider) repository.MigrationClusterRepo {
	return &MigrationClusterRepoImpl{
		dbProvider: dbProvider,
	}
}

func (m *MigrationClusterRepoImpl) Save(ctx context.Context, migrationCluster *entity.MigrationCluster) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "MigrationClusterRepoImpl.Save")
	defer span.End()

	wdb := m.dbProvider.WriteDB(ctx)
	model := cvtMigrationClusterEnToModel(migrationCluster)

	if model.ID == 0 {
		if err := wdb.Create(model).Error; err != nil {
			return bizerr.WrapGormError(err, "create migration cluster failed")
		}
	} else {
		if err := wdb.Omit("created_at").Save(model).Error; err != nil {
			return bizerr.WrapGormError(err, "save migration cluster failed")
		}
	}

	migrationCluster.Id = uint64(model.ID)

	return nil
}

func (m *MigrationClusterRepoImpl) Load(ctx context.Context, id uint64) (*entity.MigrationCluster, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "MigrationClusterRepoImpl.Load")
	defer span.End()

	rdb := m.dbProvider.ReadDB(ctx)

	var migrationCluster schema.MigrationCluster
	if err := rdb.Where("id = ?", id).First(&migrationCluster).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "load migration cluster failed")
	}

	return cvtMigrationClusterModelToEn(&migrationCluster), nil
}

func cvtMigrationClusterModelToEn(model *schema.MigrationCluster) *entity.MigrationCluster {
	out := &entity.MigrationCluster{
		Id:                           uint64(model.ID),
		SourceClusterId:              model.SourceClusterID,
		BackupId:                     model.BackupID,
		TargetProjectId:              model.TargetProjectID,
		ClusterDisplayName:           model.ClusterDisplayName,
		TidbNodeSetting:              nil, // fill later
		TikvNodeSetting:              nil, // fill later
		TiflashNodeSetting:           nil, // fill later
		BrVersion:                    model.BrVersion,
		BrImageTag:                   model.BrImageTag,
		BrCustomOptions:              model.BrCustomOptions,
		TargetClusterVersion:         model.TargetClusterVersion,
		State:                        tidbmgmtservicev1.MigrationCluster_State(tidbmgmtservicev1.MigrationCluster_State_value[model.State]),
		TargetClusterId:              model.TargetClusterID,
		TidbImageTag:                 model.TidbImageTag,
		TikvImageTag:                 model.TikvImageTag,
		PdImageTag:                   model.PdImageTag,
		TiflashImageTag:              model.TiflashImageTag,
		CreateClusterTidbImageTag:    model.CreateClusterTidbImageTag,
		CreateClusterTikvImageTag:    model.CreateClusterTikvImageTag,
		CreateClusterPdImageTag:      model.CreateClusterPdImageTag,
		CreateClusterTiflashImageTag: model.CreateClusterTiflashImageTag,
	}
	if model.RestoreMode != "" {
		if v, err := enumutil.FromString[tidbmgmtservicev1.MigrationCluster_RestoreMode](model.RestoreMode); err == nil {
			out.RestoreMode = tidbmgmtservicev1.MigrationCluster_RestoreMode(v)
		}
	}

	if model.TargetRegionID != "" {
		if v, err := enumutil.FromString[globalcommonv1.Region_Name](model.TargetRegionID); err == nil {
			out.TargetRegionId = globalcommonv1.Region_Name(v)
		}
	}
	if model.BackupRegionID != "" {
		if v, err := enumutil.FromString[globalcommonv1.Region_Name](model.BackupRegionID); err == nil {
			out.BackupRegionId = globalcommonv1.Region_Name(v)
		}
	}

	if model.ClusterTopology != nil {
		if model.ClusterTopology.TidbNodeSetting != nil {
			tidbNodeGroups := make([]*tidbmgmtservicev1.MigrationCluster_TidbNodeGroup, 0, len(model.ClusterTopology.TidbNodeSetting.TidbNodeGroups))
			for _, group := range model.ClusterTopology.TidbNodeSetting.TidbNodeGroups {
				tidbNodeGroups = append(tidbNodeGroups, &tidbmgmtservicev1.MigrationCluster_TidbNodeGroup{
					DisplayName:    group.DisplayName,
					IsDefaultGroup: group.IsDefaultGroup,
					NodeCount:      group.NodeCount,
				})
			}
			out.TidbNodeSetting = &tidbmgmtservicev1.MigrationCluster_TidbNodeSetting{
				NodeSpecKey:    model.ClusterTopology.TidbNodeSetting.NodeSpecKey,
				TidbNodeGroups: tidbNodeGroups,
			}
		}
		if model.ClusterTopology.TikvNodeSetting != nil {
			out.TikvNodeSetting = &tidbmgmtservicev1.MigrationCluster_StorageNodeSetting{
				NodeSpecKey: model.ClusterTopology.TikvNodeSetting.NodeSpecKey,
				NodeCount:   model.ClusterTopology.TikvNodeSetting.NodeCount,
			}
		}
		if model.ClusterTopology.TiflashNodeSetting != nil {
			out.TiflashNodeSetting = &tidbmgmtservicev1.MigrationCluster_StorageNodeSetting{
				NodeSpecKey: model.ClusterTopology.TiflashNodeSetting.NodeSpecKey,
				NodeCount:   model.ClusterTopology.TiflashNodeSetting.NodeCount,
			}
		}
	}

	return out
}

func cvtMigrationClusterEnToModel(en *entity.MigrationCluster) *schema.MigrationCluster {
	out := &schema.MigrationCluster{
		Model: gorm.Model{
			ID: uint(en.Id),
		},
		SourceClusterID:              en.SourceClusterId,
		BackupID:                     en.BackupId,
		TargetProjectID:              en.TargetProjectId,
		TargetClusterID:              en.TargetClusterId,
		ClusterDisplayName:           en.ClusterDisplayName,
		ClusterTopology:              &schema.ClusterTopology{},
		BrVersion:                    en.BrVersion,
		BrImageTag:                   en.BrImageTag,
		BrCustomOptions:              en.BrCustomOptions,
		State:                        en.State.String(),
		TargetClusterVersion:         en.TargetClusterVersion,
		TidbImageTag:                 en.TidbImageTag,
		TikvImageTag:                 en.TikvImageTag,
		PdImageTag:                   en.PdImageTag,
		TiflashImageTag:              en.TiflashImageTag,
		CreateClusterTidbImageTag:    en.CreateClusterTidbImageTag,
		CreateClusterTikvImageTag:    en.CreateClusterTikvImageTag,
		CreateClusterPdImageTag:      en.CreateClusterPdImageTag,
		CreateClusterTiflashImageTag: en.CreateClusterTiflashImageTag,
	}
	if en.GetRestoreMode() != tidbmgmtservicev1.MigrationCluster_MODE_UNSPECIFIED {
		out.RestoreMode = en.GetRestoreMode().String()
	}

	if en.TargetRegionId != globalcommonv1.Region_NAME_UNSPECIFIED {
		out.TargetRegionID = en.TargetRegionId.String()
	}
	if en.BackupRegionId != globalcommonv1.Region_NAME_UNSPECIFIED {
		out.BackupRegionID = en.BackupRegionId.String()
	}

	if en.TidbNodeSetting != nil {
		tidbNodeGroups := make([]schema.TidbNodeGroup, 0, len(en.TidbNodeSetting.TidbNodeGroups))
		for _, group := range en.TidbNodeSetting.TidbNodeGroups {
			tidbNodeGroups = append(tidbNodeGroups, schema.TidbNodeGroup{
				DisplayName:    group.DisplayName,
				IsDefaultGroup: group.IsDefaultGroup,
				NodeCount:      group.NodeCount,
			})
		}
		out.ClusterTopology.TidbNodeSetting = &schema.TidbNodeSetting{
			NodeSpecKey:    en.TidbNodeSetting.NodeSpecKey,
			TidbNodeGroups: tidbNodeGroups,
		}
	}
	if en.TikvNodeSetting != nil {
		out.ClusterTopology.TikvNodeSetting = &schema.StorageNodeSetting{
			NodeSpecKey: en.TikvNodeSetting.NodeSpecKey,
			NodeCount:   en.TikvNodeSetting.NodeCount,
		}
	}
	if en.TiflashNodeSetting != nil {
		out.ClusterTopology.TiflashNodeSetting = &schema.StorageNodeSetting{
			NodeSpecKey: en.TiflashNodeSetting.NodeSpecKey,
			NodeCount:   en.TiflashNodeSetting.NodeCount,
		}
	}

	return out
}
