package repository

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/tidbcloud/pkgv2/db"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
)

type lockRepoImpl struct {
	dbProvider db.TransProvider
}

func NewLockRepo(dbProvider db.TransProvider) repository.LockRepo {
	return &lockRepoImpl{
		dbProvider: dbProvider,
	}
}

func (l *lockRepoImpl) Lock(ctx context.Context, lock *entity.Lock) error {
	ctx, span := tracer.Start(ctx, "LockRepo.Lock")
	defer span.End()

	wdb := l.dbProvider.WriteDB(ctx)

	var out sql.NullBool
	if err := wdb.Raw(`SELECT GET_LOCK(?,?)`, lock.Key, lock.TimeoutSecs).Scan(&out).Error; err != nil {
		return err
	}

	if !out.Valid || !out.Bool {
		return fmt.Errorf("failed to get lock %s", lock.Key)
	}

	return nil
}

func (l *lockRepoImpl) IsUsed(ctx context.Context, lock *entity.Lock) (int64, error) {
	ctx, span := tracer.Start(ctx, "LockRepo.IsUsed")
	defer span.End()

	var out sql.NullInt64
	rdb := l.dbProvider.ReadDB(ctx)
	if err := rdb.Raw(`SELECT IS_USED_LOCK(?)`, lock.Key).Scan(&out).Error; err != nil {
		return 0, err
	}
	if !out.Valid {
		return 0, nil
	}
	return out.Int64, nil
}

func (l *lockRepoImpl) IsFree(ctx context.Context, lock *entity.Lock) (bool, error) {
	ctx, span := tracer.Start(ctx, "LockRepo.IsFree")
	defer span.End()

	var out sql.NullBool
	rdb := l.dbProvider.ReadDB(ctx)
	if err := rdb.Raw(`SELECT IS_FREE_LOCK(?)`, lock.Key).Scan(&out).Error; err != nil {
		return false, err
	}
	if !out.Valid {
		return false, nil
	}
	return out.Bool, nil
}

func (l *lockRepoImpl) Release(ctx context.Context, lock *entity.Lock) error {
	ctx, span := tracer.Start(ctx, "LockRepo.Release")
	defer span.End()

	wdb := l.dbProvider.WriteDB(ctx)

	var out sql.NullBool
	if err := wdb.Raw(`SELECT RELEASE_LOCK(?)`, lock.Key).Scan(&out).Error; err != nil {
		return err
	}

	if out.Valid && !out.Bool {
		return fmt.Errorf("failed to release lock %s", lock.Key)
	}

	return nil
}
