package repo_imp

import (
	"context"
	"errors"
	"time"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/repo"
	"go.opentelemetry.io/otel"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

var Tracer = otel.Tracer("repo_imp")

type regionMeteringRepoImp struct {
	dbProvider db.Provider
}

func NewRegionMeteringRepo(
	dbProvider db.Provider,
) repo.RegionMeteringRepo {
	return &regionMeteringRepoImp{dbProvider}
}

func (r *regionMeteringRepoImp) Load(
	ctx context.Context,
	region string,
	typ string,
) (_ *entity.RegionMeteringRecord, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "regionMeteringRepo.Load")
	defer func() { tracing.EndSpan(span, err) }()

	record := &entity.RegionMeteringRecord{}
	rdb := r.dbProvider.ReadDB(ctx)
	if e := rdb.Model(&entity.RegionMeteringRecord{}).
		Where("region=? and type=?", region, typ).First(record).Error; e != nil {
		return nil, bizerr.WrapGormError(e, "failed to find RegionMeteringRecord of region %s", region)
	}

	return record, nil
}

func (r *regionMeteringRepoImp) Save(ctx context.Context, record *entity.RegionMeteringRecord) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "regionMeteringRepo.Save")
	defer func() { tracing.EndSpan(span, err) }()

	if record == nil {
		return nil
	}

	wdb := r.dbProvider.WriteDB(ctx)
	if e := wdb.Save(record).Error; e != nil {
		return bizerr.WrapGormError(e, "failed to save RegionMeteringRecord of region %s", record.Region)
	}

	return nil
}

type instanceCapacityCheckpointRepoImpl struct {
	dbProvider db.Provider
}

func NewInstanceCapacityCheckpointRepo(
	dbProvider db.Provider,
) repo.InstanceCapacityCheckpointRepo {
	return &instanceCapacityCheckpointRepoImpl{dbProvider}
}

func (r *instanceCapacityCheckpointRepoImpl) Load(ctx context.Context) (int64, error) {
	var record entity.InstanceCapacityCheckpoint
	rdb := r.dbProvider.ReadDB(ctx)
	err := rdb.Where("id = 1").First(&record).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, nil
		}
		return 0, err
	}
	return record.Checkpoint, nil
}

func (r *instanceCapacityCheckpointRepoImpl) Save(ctx context.Context, checkpoint int64) error {
	wdb := r.dbProvider.WriteDB(ctx)
	now := time.Now().UTC()
	record := entity.InstanceCapacityCheckpoint{
		ID:         1,
		Checkpoint: checkpoint,
		UpdatedAt:  &now,
	}
	return wdb.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "id"}},
		DoUpdates: clause.AssignmentColumns([]string{"checkpoint", "updated_at"}),
	}).Create(&record).Error
}
