package metering

import (
	"context"
	"time"

	"github.com/tidbcloud/pkgv2/db"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	billingrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/metering"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type billingReportRecordRepoImp struct {
	dbProvider db.Provider
}

func NewBillingReportRecordRepo(dbProvider db.Provider) billingrepo.ReportRecordRepo {
	return billingReportRecordRepoImp{
		dbProvider: dbProvider,
	}
}

func (p billingReportRecordRepoImp) LoadUnsyncedRecords(
	ctx context.Context,
	cursor uint64,
	limit int,
) ([]*entity.BillingReportRecord, bizerr.BizError) {
	rdb := p.dbProvider.ReadDB(ctx)
	var records []*schema.BillingReportRecord
	if err := rdb.Model(&schema.BillingReportRecord{}).
		Where("id>? and status=?", cursor, entity.BillingRecordStatusUnsynced).
		Order("id asc").
		Limit(limit).Find(&records).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "find billing report records")
	}

	out := make([]*entity.BillingReportRecord, 0, len(records))
	for _, each := range records {
		out = append(out, ReportRecordModelToEn(each))
	}
	return out, nil
}

func (p billingReportRecordRepoImp) Save(ctx context.Context, records ...*entity.BillingReportRecord) bizerr.BizError {
	if len(records) == 0 {
		return nil
	}
	wdb := p.dbProvider.WriteDB(ctx)
	for _, each := range records {
		recordModel := ReportRecordEnToModel(each)
		if err := wdb.Save(recordModel).Error; err != nil {
			return bizerr.WrapGormError(err, "save billingReportRecord, tenantID=%d, record=%+v", each.TenantID, each)
		}
	}
	return nil
}

func (p billingReportRecordRepoImp) MarkSynced(ctx context.Context, ids ...uint64) bizerr.BizError {
	if len(ids) == 0 {
		return nil
	}
	wdb := p.dbProvider.WriteDB(ctx)

	if err := wdb.Model(&schema.BillingReportRecord{}).Where("id in (?) AND status=?", ids, entity.BillingRecordStatusUnsynced).
		Update("status", entity.BillingRecordStatusSynced).Error; err != nil {
		return bizerr.WrapGormError(err, "mark billing report record synced, ids=%v", ids)
	}
	return nil
}

func (p billingReportRecordRepoImp) Clean(ctx context.Context, before time.Time, limit int) (int64, bizerr.BizError) {
	wdb := p.dbProvider.WriteDB(ctx).Unscoped()
	result := wdb.Where("created_at < ? AND status = 'synced'", before).
		Limit(limit).
		Delete(&schema.BillingReportRecord{})
	if result.Error != nil {
		return 0, bizerr.WrapGormError(result.Error, "clean billing report record")
	}
	return result.RowsAffected, nil
}
