package dbutil

import (
	"errors"
	"fmt"

	"github.com/pingcap/errcode"
	"github.com/tidbcloud/metrics-server/central/db/entity"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// PreloadCluster preloads the dependencies of entity.Cluster
func PreloadCluster(tx *gorm.DB) *gorm.DB {
	// see limitation, we can not use go-queryset
	// https://github.com/jirfag/go-queryset/issues/33
	return tx.Preload("ComponentsV2").
		Preload("ComponentsV2.Region").
		Preload("ComponentsV2.InternalProfile").
		Preload("ComponentsV2.InternalProfile.InfraCompute").
		Preload("Connections").
		Preload("Provider").
		Preload("CreatedBy").
		Preload("Tenant").
		Preload("Project")
}

// PreloadClusterV2 preloads the dependencies of entity.Cluster
func PreloadClusterV2(tx *gorm.DB) *gorm.DB {
	// see limitation, we can not use go-queryset
	// https://github.com/jirfag/go-queryset/issues/33
	return tx.Preload("ComponentsV2").
		Preload("ComponentsV2.Region").
		Preload("ComponentsV2.InternalProfile").
		Preload("ComponentsV2.InternalProfile.InfraCompute").
		Preload("Connections").
		Preload("Provider")
}

// TagNotFound wraps the `gorm.ErrRecordNotFound` to a new error type.
func TagNotFound(err error) error {
	if err == gorm.ErrRecordNotFound {
		return errcode.NewNotFoundErr(err)
	}
	return err
}

type Result struct {
	ComponentName string
	Total         uint32
}

func GetActiveClusterCount(tx *gorm.DB, tenantID entity.ID) (int64, error) {
	var count int64
	return count, tx.Clauses(clause.Locking{Strength: "UPDATE"}).Table("clusters").Where(" clusters.tenant_id=? and clusters.status not in (?) ", tenantID, entity.DeletedOrClearedClusterStatus).Count(&count).Error
}

func GenerateAutoBackupName(clusterID entity.ID) string {
	return fmt.Sprintf("ab-%d", clusterID)
}

func CheckResourceOrgIDAndProjectID(orgID, resourceOrgID, projectID, resourceProjectID entity.ID) error {
	if orgID != resourceOrgID || projectID != resourceProjectID {
		return errcode.NewForbiddenErr(errors.New("you do not have permission to access this resource"))
	}
	return nil
}

func UserInProject(userProjects []entity.Project, projectID entity.ID) bool {
	userInProject := false
	for _, project := range userProjects {
		if project.ID == projectID {
			userInProject = true
		}
	}
	return userInProject
}

func CheckProjectID(userProjects []entity.Project, projectID entity.ID) error {
	userInProject := UserInProject(userProjects, projectID)
	if userInProject {
		return nil
	}

	return errcode.NewForbiddenErr(errors.New("you do not have permission to access this project"))
}

func GetRegionFromClusterComponents(clusterComponents []*entity.ClusterComponent) (entity.Region, error) {
	if len(clusterComponents) == 0 {
		return entity.Region{}, errors.New("cluster component list is empty")
	}

	id2Region := make(map[uint64]entity.Region, len(clusterComponents))
	for _, each := range clusterComponents {
		if each == nil || each.Region == nil {
			return entity.Region{}, errors.New("invalid element in cluster component list")
		}
		id2Region[each.RegionID.Uint64()] = *each.Region
	}

	regions := make([]entity.Region, 0, len(id2Region))
	for _, each := range id2Region {
		regions = append(regions, each)
	}

	if len(regions) != 1 {
		return entity.Region{}, errors.New("only support one region per cluster")
	}

	return regions[0], nil
}
