package repo

import (
	"context"
	"fmt"

	"gorm.io/gorm"

	"github.com/tidbcloud/devops/entity"
	"github.com/tidbcloud/devops/models"
	contutil "github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/configcenter/repo/cvt"
	"github.com/tidbcloud/pkgv2/db"
)

type Driver interface {
	Save(ctx context.Context, secondary *entity.ConfigAttributes) (*entity.ConfigAttributes, error)
	Query(ctx context.Context, opts ...repoopt.Option) ([]*entity.ConfigAttributes, error)
	Load(ctx context.Context, paramName string, version int64) (*entity.ConfigAttributes, error)
	Create(ctx context.Context, attributes []*entity.ConfigAttributes) error
}

type configCenterRepoDriver struct {
	cvter      *cvt.Converter
	dbProvider db.Provider
}

func (c *configCenterRepoDriver) Save(ctx context.Context, configAttribute *entity.ConfigAttributes) (*entity.ConfigAttributes, error) {
	wdb := c.dbProvider.WriteDB(ctx)

	configAttributeMod := c.cvter.EntityToModel().ConfigCenter(configAttribute)
	err := wdb.Session(&gorm.Session{FullSaveAssociations: true}).Save(configAttributeMod).Error
	if err != nil {
		return nil, fmt.Errorf("save config attribute %s for param %s for version %d  failed: %w",
			*configAttribute.ApplyPolicy,
			configAttribute.ParamName,
			configAttribute.Version,
			err)
	}

	return c.cvter.ModelToEntity().ConfigCenter(configAttributeMod), nil
}

func (c *configCenterRepoDriver) Create(ctx context.Context, attributes []*entity.ConfigAttributes) error {
	wdb := c.dbProvider.WriteDB(ctx)
	mods := make([]*models.ConfigAttributesV2, 0)
	for _, v := range attributes {
		mods = append(mods, c.cvter.EntityToModel().ConfigCenter(v))
	}
	err := wdb.Session(&gorm.Session{FullSaveAssociations: true}).Create(mods).Error
	if err != nil {
		return fmt.Errorf("create config attributes failed")
	}
	return nil
}

func (c *configCenterRepoDriver) Load(ctx context.Context, paramName string, version int64) (*entity.ConfigAttributes, error) {
	rdb := c.dbProvider.ReadDB(ctx)
	configAttributeMod := &models.ConfigAttributesV2{}

	err := rdb.Model(&models.ConfigAttributesV2{}).
		Where("param_name = ?", paramName).
		Where("version = ?", version).
		First(configAttributeMod).
		Error
	if err != nil {
		return nil, fmt.Errorf("load config attribute for param %s, version %d failed: %w", paramName, version, err)
	}

	return c.cvter.ModelToEntity().ConfigCenter(configAttributeMod), nil
}

func (c *configCenterRepoDriver) Query(ctx context.Context, opts ...repoopt.Option) ([]*entity.ConfigAttributes, error) {
	rdb := c.dbProvider.ReadDB(ctx)
	configAttributes := []*models.ConfigAttributesV2{}

	rdb = rdb.Model(&models.ConfigAttributesV2{})

	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	err := rdb.Find(&configAttributes).Error
	if err != nil {
		return nil, fmt.Errorf("query config attributes failed: %w", err)
	}

	return contutil.TransformSlice(configAttributes, c.cvter.ModelToEntity().ConfigCenter), nil
}
