package dbutil

import (
	"errors"
	"fmt"

	"github.com/pingcap/errcode"
	"github.com/tidbcloud/central-sdk/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("Components").
		Preload("Connections").
		Preload("KMSs").
		Preload("Provider").
		Preload("CreatedBy").
		Preload("ClusterProfile").
		Preload("Tenant").
		Preload("Components.ComponentMachines", "status NOT IN (?)", entity.DeletedComponent).
		Preload("Components.ComponentMachines.Machine")
}

// 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 GetUsedComponentsCount(tx *gorm.DB, tenantID entity.ID) (uint32, uint32, uint32, error) {
	var results []Result
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Table("component_machines").Select("count(*) as total, components.component_name as component_name ").
		Joins(" join components on components.id=component_machines.component_id").
		Joins(" join clusters on clusters.id=components.cluster_id").
		Group("components.component_name").
		Where(" clusters.tenant_id=? and clusters.status not in (?) and component_machines.status !=?", tenantID.Uint64(), entity.DeletedOrClearedClusterStatus, entity.DeletedComponent).Scan(&results).Error; err != nil {
		return 0, 0, 0, err
	}
	var usedTidbCount, usedTikvCount, usedTiflashCount uint32
	for _, item := range results {
		switch item.ComponentName {
		case string(entity.TiDB):
			usedTidbCount = item.Total
		case string(entity.TiKV):
			usedTikvCount = item.Total
		case string(entity.TiFlash):
			usedTiflashCount = item.Total
		}
	}
	return usedTidbCount, usedTikvCount, usedTiflashCount, nil
}

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