package ops_maintenance_window

import (
	"context"
	"fmt"

	maintent "github.com/tidbcloud/devops/entity/maintenance"

	maintmod "github.com/tidbcloud/devops/models/maintenance"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/clustermaintenance/repo/cvt"
	"gorm.io/gorm"

	"github.com/tidbcloud/pkgv2/db"
)

type Driver interface {
	Save(ctx context.Context, window *maintent.ClusterOpsMaintenanceWindow) (*maintent.ClusterOpsMaintenanceWindow, error)
	Load(ctx context.Context, clusterID string) (*maintent.ClusterOpsMaintenanceWindow, error)
	Query(ctx context.Context, opts ...repoopt.Option) ([]*maintent.ClusterOpsMaintenanceWindow, error)
	Count(ctx context.Context, opts ...repoopt.Option) (int64, error)
	Delete(ctx context.Context, clusterID string) error
}

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

func (d *driver) Save(
	ctx context.Context,
	window *maintent.ClusterOpsMaintenanceWindow,
) (*maintent.ClusterOpsMaintenanceWindow, error) {
	wdb := d.dbProvider.WriteDB(ctx)
	windowMod := d.cvter.EntityToModel().ClusterOpsMaintenanceWindow(window)

	err := wdb.Session(&gorm.Session{FullSaveAssociations: true}).Save(windowMod).Error
	if err != nil {
		return nil, fmt.Errorf("save cluster ops maintenance window failed: %w", err)
	}

	return d.cvter.ModelToEntity().ClusterOpsMaintenanceWindow(windowMod), nil
}

func (d *driver) Load(
	ctx context.Context,
	clusterID string,
) (*maintent.ClusterOpsMaintenanceWindow, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	windowMod := &maintmod.ClusterOpsMaintenanceWindow{}
	err := buildOpsMaintenanceWindowModel(rdb).Where("cluster_id = ?", clusterID).First(windowMod).Error
	if err != nil {
		return nil, fmt.Errorf("load cluster ops maintenance window by cluster id %s failed: %w", clusterID, err)
	}

	return d.cvter.ModelToEntity().ClusterOpsMaintenanceWindow(windowMod), nil
}

func (d *driver) Query(
	ctx context.Context,
	opts ...repoopt.Option,
) ([]*maintent.ClusterOpsMaintenanceWindow, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	windowMods := []*maintmod.ClusterOpsMaintenanceWindow{}

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

	err := rdb.Find(&windowMods).Error
	if err != nil {
		return nil, fmt.Errorf("query cluster ops maintenance windows failed: %w", err)
	}

	return container.TransformSlice(windowMods, d.cvter.ModelToEntity().ClusterOpsMaintenanceWindow), nil
}

func (d *driver) Count(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	db := d.dbProvider.ReadDB(ctx).Model(&maintmod.ClusterOpsMaintenanceWindow{})
	db = repoopt.Aggregate(opts...).Apply(db)

	var count int64
	err := db.Count(&count).Error
	if err != nil {
		return 0, fmt.Errorf("count cluster ops maintenance windows failed: %w", err)
	}

	return count, nil
}

func (d *driver) Delete(ctx context.Context, clusterID string) error {
	wdb := d.dbProvider.WriteDB(ctx)
	result := buildOpsMaintenanceWindowModel(wdb).Where("cluster_id = ?", clusterID).Delete(&maintmod.ClusterOpsMaintenanceWindow{})
	if result.Error != nil {
		return fmt.Errorf("delete cluster ops maintenance window by cluster id %s failed: %w", clusterID, result.Error)
	}
	return nil
}

func buildOpsMaintenanceWindowModel(db *gorm.DB) *gorm.DB {
	return db.Model(&maintmod.ClusterOpsMaintenanceWindow{})
}
