package restore

import (
	"context"
	"errors"

	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/gormutil"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

type scaleTikvDecisionRepoImp struct {
	provider db.Provider
}

func NewScaleTikvDecisionRepo(provider db.Provider) repo.ScaleTikvDecisionRepo {
	return &scaleTikvDecisionRepoImp{
		provider: provider,
	}
}

func (r *scaleTikvDecisionRepoImp) Create(ctx context.Context, scaleTikvDecision *schema.ScaleTikvDecision) error {
	if scaleTikvDecision == nil {
		return status.Errorf(codes.InvalidArgument, "ScaleTikvDecision is nil")
	}
	if scaleTikvDecision.SharedPoolID == "" {
		return status.Errorf(codes.InvalidArgument, "SharedPoolID cannot be empty")
	}
	if scaleTikvDecision.BizID == "" {
		return status.Errorf(codes.InvalidArgument, "BizID cannot be empty")
	}
	if scaleTikvDecision.BizType == "" {
		return status.Errorf(codes.InvalidArgument, "BizType cannot be empty")
	}
	if scaleTikvDecision.Status == "" {
		return status.Errorf(codes.InvalidArgument, "Status cannot be empty")
	}
	if scaleTikvDecision.BaseOn == "" {
		return status.Errorf(codes.InvalidArgument, "BaseOn cannot be empty")
	}

	wdb := r.provider.WriteDB(ctx)
	if err := wdb.Create(scaleTikvDecision).Error; err != nil {
		if errors.Is(err, gorm.ErrDuplicatedKey) || gormutil.IsDuplicatedError(err) {
			return status.Errorf(codes.AlreadyExists, "ScaleTikvDecision %s/%s/%s already exists", scaleTikvDecision.SharedPoolID, scaleTikvDecision.BizType, scaleTikvDecision.BizID)
		}
		return status.Errorf(codes.Internal, "failed to create ScaleTikvDecision: %v", err)
	}
	return nil
}

func (r *scaleTikvDecisionRepoImp) Save(ctx context.Context, scaleTikvDecision *schema.ScaleTikvDecision) error {
	if scaleTikvDecision == nil {
		return status.Errorf(codes.InvalidArgument, "ScaleTikvDecision is nil")
	}
	if scaleTikvDecision.ID == 0 {
		return status.Errorf(codes.InvalidArgument, "ScaleTikvDecision ID cannot be zero")
	}
	if scaleTikvDecision.SharedPoolID == "" {
		return status.Errorf(codes.InvalidArgument, "SharedPoolID cannot be empty")
	}
	if scaleTikvDecision.BizID == "" {
		return status.Errorf(codes.InvalidArgument, "BizID cannot be empty")
	}
	if scaleTikvDecision.BizType == "" {
		return status.Errorf(codes.InvalidArgument, "BizType cannot be empty")
	}
	if scaleTikvDecision.Status == "" {
		return status.Errorf(codes.InvalidArgument, "Status cannot be empty")
	}
	if scaleTikvDecision.BaseOn == "" {
		return status.Errorf(codes.InvalidArgument, "BaseOn cannot be empty")
	}

	wdb := r.provider.WriteDB(ctx)
	if err := wdb.Save(scaleTikvDecision).Error; err != nil {
		return status.Errorf(codes.Internal, "failed to save ScaleTikvDecision: %v", err)
	}
	return nil
}

func (r *scaleTikvDecisionRepoImp) Load(ctx context.Context, id uint, includeDeleted bool) (*schema.ScaleTikvDecision, error) {
	if id == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "ScaleTikvDecision id cannot be zero")
	}

	rdb := r.provider.ReadDB(ctx)
	if includeDeleted {
		rdb = rdb.Unscoped()
	}
	var scaleTikvDecision schema.ScaleTikvDecision

	if err := rdb.Where("id = ?", id).First(&scaleTikvDecision).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Errorf(codes.NotFound, "ScaleTikvDecision with id %d not found", id)
		}
		return nil, status.Errorf(codes.Internal, "failed to load ScaleTikvDecision: %v", err)
	}

	return &scaleTikvDecision, nil
}

func (r *scaleTikvDecisionRepoImp) Delete(ctx context.Context, id uint) error {
	if id == 0 {
		return status.Errorf(codes.InvalidArgument, "ScaleTikvDecision id cannot be zero")
	}

	wdb := r.provider.WriteDB(ctx)

	if err := wdb.Where("id = ?", id).Delete(&schema.ScaleTikvDecision{}).Error; err != nil {
		return status.Errorf(codes.Internal, "failed to delete ScaleTikvDecision: %v", err)
	}

	return nil
}

func (r *scaleTikvDecisionRepoImp) List(ctx context.Context, filter *repo.ScaleTikvDecisionFilter) ([]*schema.ScaleTikvDecision, error) {
	rdb := r.provider.ReadDB(ctx)
	if filter == nil {
		filter = &repo.ScaleTikvDecisionFilter{}
	}
	if filter.IncludeDeleted {
		rdb = rdb.Unscoped()
	}

	query := rdb.Model(&schema.ScaleTikvDecision{})
	if filter.SharedPoolID != "" {
		query = query.Where("shared_pool_id = ?", filter.SharedPoolID)
	}
	if filter.BizType != "" {
		query = query.Where("biz_type = ?", filter.BizType)
	}
	if filter.BizID != "" {
		query = query.Where("biz_id = ?", filter.BizID)
	}
	if len(filter.IncludeStatuses) > 0 {
		query = query.Where("status IN ?", filter.IncludeStatuses)
	}
	if filter.StartTime != nil {
		query = query.Where("created_at >= ?", filter.StartTime)
	}

	var scaleTikvDecisions []*schema.ScaleTikvDecision
	if err := query.Find(&scaleTikvDecisions).Error; err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list ScaleTikvDecision: %v", err)
	}

	return scaleTikvDecisions, nil
}
