package publicconnectionsetting

import (
	"context"

	"github.com/samber/lo"
	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/publicconnectionsetting"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

var tracer = otel.Tracer("infra.publicconnectionsetting")

func NewPublicConnectionSettingRepo(provider db.Provider) publicconnectionsetting.Repo {
	return &repoImpl{
		provider: provider,
	}
}

type repoImpl struct {
	provider db.Provider
}

func (r *repoImpl) Save(ctx context.Context, setting *entity.PublicConnectionSetting) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "publicconnectionsetting.Save")
	defer span.End()

	schemaSetting := enToModel(setting)
	wdb := r.provider.WriteDB(ctx)

	var existingSetting schema.PublicConnectionSetting

	if rawErr := wdb.Model(&schema.PublicConnectionSetting{}).
		Where("`cluster_id` = ?", setting.ClusterId).
		First(&existingSetting).Error; rawErr != nil {
		err := bizerr.WrapGormError(rawErr, "load public connection setting")
		if err.ErrCode() != bizerr.ErrCodeRecordNotFound {
			return err
		}
		// create if not exists
		if rawErr := wdb.Create(schemaSetting).Error; rawErr != nil {
			return bizerr.WrapGormError(rawErr, "create public connection setting")
		}

		return nil
	}

	// update if exists
	schemaSetting.ID = existingSetting.ID
	if rawErr := wdb.Omit("created_at").Save(schemaSetting).Error; rawErr != nil {
		return bizerr.WrapGormError(rawErr, "save public connection setting")
	}

	return nil
}

func (r *repoImpl) LoadByClusterId(
	ctx context.Context,
	clusterId uint64,
) ([]*entity.PublicConnectionSetting, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "publicconnectionsetting.LoadByClusterId")
	defer span.End()

	var schemaSettings []schema.PublicConnectionSetting
	if rawErr := r.provider.ReadDB(ctx).Model(&schema.PublicConnectionSetting{}).
		Where("`cluster_id` = ?", clusterId).
		Find(&schemaSettings).Error; rawErr != nil {
		return nil, bizerr.WrapGormError(rawErr, "load public connection settings")
	}

	out := lo.Map(schemaSettings, func(each schema.PublicConnectionSetting, _ int) *entity.PublicConnectionSetting {
		return modelToEn(&each)
	})
	return out, nil
}

func (r *repoImpl) GetByTidbGroupId(
	ctx context.Context,
	tidbGroupId uint64,
) (*entity.PublicConnectionSetting, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "publicconnectionsetting.GetByTidbGroupId")
	defer span.End()

	var schemaSetting schema.PublicConnectionSetting
	if rawErr := r.provider.ReadDB(ctx).Model(&schema.PublicConnectionSetting{}).
		Where("`tidb_group_id` = ?", tidbGroupId).
		First(&schemaSetting).Error; rawErr != nil {
		return nil, bizerr.WrapGormError(rawErr, "load public connection setting")
	}

	return modelToEn(&schemaSetting), nil
}

func (r *repoImpl) DeleteByClusterID(ctx context.Context, clusterID uint64) bizerr.BizError {
	wdb := r.provider.WriteDB(ctx)
	if err := wdb.Where("`cluster_id` = ?", clusterID).Delete(&schema.PublicConnectionSetting{}).Error; err != nil {
		return bizerr.WrapGormError(err, "delete public connection setting by cluster id")
	}
	return nil
}

func (r *repoImpl) DeleteByTidbGroupId(ctx context.Context, tidbGroupId uint64) bizerr.BizError {
	wdb := r.provider.WriteDB(ctx)
	if err := wdb.Where("`tidb_group_id` = ?", tidbGroupId).Delete(&schema.PublicConnectionSetting{}).Error; err != nil {
		return bizerr.WrapGormError(err, "delete public connection setting by tidb group id")
	}
	return nil
}
