package cluster

// Database access should be done through this file.
import (
	"context"

	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/dbutil"
	"github.com/pingcap/dbaas/central/db/entity"
	"gorm.io/gorm"
)

type ClusterDao struct {
}

// migrate db function from db layer to dao layer but use only one copy of code
// TODO: keep migration
func (d *ClusterDao) FindClusters(filter dbutil.ClusterFilter) (clusters []entity.Cluster, err error) {
	// see limitation, we can not use go-queryset
	// https://github.com/jirfag/go-queryset/issues/33
	err = db.Transaction(func(tx *gorm.DB) error {
		clusters, err = dbutil.FindClusters(tx, filter)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return clusters, nil
}

func (d *ClusterDao) GetCluster(clusterID entity.ID) (cluster entity.Cluster, err error) {
	err = db.Transaction(func(tx *gorm.DB) error {
		cluster, err = dbutil.GetCluster(tx, clusterID)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return cluster, err
	}
	return cluster, nil
}

func (d *ClusterDao) GetClusterRegion(ctx context.Context, cluster entity.Cluster) (region *entity.Region, err error) {
	err = db.Transaction(func(tx *gorm.DB) error {
		region, err = dbutil.GetClusterRegion(ctx, tx, cluster)
		if err != nil {
			return err
		}
		return nil
	})
	return region, err
}

// UpdateCluster updates the requeire field of cluster
// if forceSync is `false` means some field do not want to be update in some scene, the trick logic is write in a transaction
func (d *ClusterDao) UpdateCluster(cluster entity.Cluster, schemas []entity.ClusterDBSchemaField, forceSync bool) error {
	return db.Transaction(func(tx *gorm.DB) error {
		// tricker deal with the ReadOnly status for keep the consistency
		haveReadonly := false
		for _, s := range schemas {
			if s == entity.ClusterDBSchema.ReadOnly {
				haveReadonly = true
			}
		}
		if haveReadonly && !forceSync {
			tmpCluster, err := d.GetCluster(cluster.ID)
			if err != nil {
				return err
			}
			if tmpCluster.ReadOnly&entity.ForceReadOnlyStatus != 0 {
				cluster.ReadOnly |= entity.ForceReadOnlyStatus
			}
		}
		return cluster.Update(tx, schemas...)
	})
}

func (d *ClusterDao) GetProjectCIDRByClusterID(projectID, clusterID entity.ID) (*entity.ProjectNetwork, error) {
	var (
		cluster        entity.Cluster
		projectNetwork entity.ProjectNetwork
		err            error
	)

	err = db.Transaction(func(tx *gorm.DB) error {
		cluster, err = d.GetCluster(clusterID)
		if err != nil {
			return err
		}
		return entity.NewProjectNetworkQuerySet(tx).ProjectIDEq(projectID).ProviderIDEq(cluster.ProviderID).One(&projectNetwork)
	})

	return &projectNetwork, err
}
