package repo_imp

import (
	"context"
	"strings"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/repo"
)

type uploadRepoImp struct {
	dbProvider db.Provider
}

func NewUploadRepoImp(dbProvider db.Provider) repo.UploadRepo {
	return &uploadRepoImp{dbProvider}
}

func (r *uploadRepoImp) Create(ctx context.Context, record *entity.Upload) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "uploadRepo.Create")
	defer func() { tracing.EndSpan(span, err) }()

	if record == nil {
		return nil
	}

	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Create(record).Error; err != nil {
		if strings.Contains(err.Error(), "Duplicate entry") {
			return bizerr.New(bizerr.ErrCodeRecordExist, "upload %s already exists", record.ID)
		}
		return bizerr.WrapGormError(
			err,
			"failed to Create record, bucketKey: %s, clusterID: %d",
			record.BucketKey,
			record.ClusterID,
		)
	}

	return nil
}

func (r *uploadRepoImp) GetUploadByID(ctx context.Context, id string) (upload *entity.Upload, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "uploadRepo.GetUploadByID")
	defer func() { tracing.EndSpan(span, err) }()

	upload = &entity.Upload{}
	rdb := r.dbProvider.ReadDB(ctx)
	if err := rdb.Where("id = ?", id).First(upload).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to get record by id: %s", id)
	}
	return upload, nil
}

func (r *uploadRepoImp) GetUploadByIDCompat(
	ctx context.Context,
	id uint64,
) (upload *entity.Upload, err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "uploadRepo.GetUploadByID")
	defer func() { tracing.EndSpan(span, err) }()

	upload = &entity.Upload{}
	rdb := r.dbProvider.ReadDB(ctx)
	if err := rdb.Where("id_compat = ?", id).First(upload).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to get record by id_compat: %d", id)
	}
	return upload, nil
}

func (r *uploadRepoImp) UpdateStatus(
	ctx context.Context,
	id string,
	status constant.UploadStatus,
) (err bizerr.BizError) {
	ctx, span := Tracer.Start(ctx, "uploadRepo.UpdateStatus")
	defer func() { tracing.EndSpan(span, err) }()

	wdb := r.dbProvider.WriteDB(ctx)
	res := wdb.Model(&entity.Upload{}).Where("id = ?", id).Update("status", status)
	if res.Error != nil {
		return bizerr.WrapGormError(res.Error, "failed to Update record: %s", id)
	} else if res.RowsAffected == 0 {
		return bizerr.New(bizerr.ErrCodeRecordNotFound, "failed to Update record: %s", id)
	}
	return nil
}
