package cluster

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

	"github.com/tidbcloud/metrics-server/central/db"
	"github.com/tidbcloud/metrics-server/central/db/dbutil"
	"github.com/tidbcloud/metrics-server/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) CreateClusterConnection(clusterID entity.ID, connectionType entity.ClusterConnectionType, location string) (connection *entity.Connection, err error) {
	err = db.Transaction(func(tx *gorm.DB) error {
		connection, err = dbutil.CreateClusterConnection(tx, clusterID, connectionType, location)
		if err != nil {
			return err
		}
		return nil
	})
	return connection, err
}

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
}

func (d *ClusterDao) DeleteClusterConnection(connection *entity.Connection) error {
	return db.Transaction(func(tx *gorm.DB) error {
		return connection.Delete(tx)
	})
}

// 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...)
	})
}
