package lockprovider

import (
	"fmt"

	"github.com/samber/lo"
	"gitlink.org.cn/cloudream/common/pkgs/distlock"
	"gitlink.org.cn/cloudream/common/utils/lo2"
)

const (
	MetadataLockPathPrefix = "Metadata"
	MetadataCreateLock     = "Create"
)

type metadataElementLock struct {
	target     StringLockTarget
	requestIDs []string
}

type MetadataLock struct {
	createReqIDs []*metadataElementLock

	lockCompatibilityTable LockCompatibilityTable
}

func NewMetadataLock() *MetadataLock {

	metadataLock := MetadataLock{
		lockCompatibilityTable: LockCompatibilityTable{},
	}

	compTable := &metadataLock.lockCompatibilityTable

	compTable.
		Column(MetadataCreateLock, func() bool { return len(metadataLock.createReqIDs) > 0 })
	trgt := LockSpecial(func(lock distlock.Lock, testLockName string) bool {
		strTar := lock.Target.(StringLockTarget)
		return lo.NoneBy(metadataLock.createReqIDs, func(other *metadataElementLock) bool { return strTar.IsConflict(&other.target) })
	})

	compTable.MustRow(trgt)

	return &metadataLock
}

// CanLock 判断这个锁能否锁定成功
func (l *MetadataLock) CanLock(lock distlock.Lock) error {
	return l.lockCompatibilityTable.Test(lock)
}

// 锁定
func (l *MetadataLock) Lock(reqID string, lock distlock.Lock) error {
	switch lock.Name {
	case MetadataCreateLock:
		l.createReqIDs = l.addElementLock(lock, l.createReqIDs, reqID)

	default:
		return fmt.Errorf("unknow lock name: %s", lock.Name)
	}

	return nil
}

func (l *MetadataLock) addElementLock(lock distlock.Lock, locks []*metadataElementLock, reqID string) []*metadataElementLock {
	strTarget := lock.Target.(StringLockTarget)
	lck, ok := lo.Find(locks, func(l *metadataElementLock) bool { return strTarget.IsConflict(&l.target) })
	if !ok {
		lck = &metadataElementLock{
			target: strTarget,
		}
		locks = append(locks, lck)
	}

	lck.requestIDs = append(lck.requestIDs, reqID)
	return locks
}

// 解锁
func (l *MetadataLock) Unlock(reqID string, lock distlock.Lock) error {
	switch lock.Name {
	case MetadataCreateLock:
		l.createReqIDs = l.removeElementLock(lock, l.createReqIDs, reqID)

	default:
		return fmt.Errorf("unknow lock name: %s", lock.Name)
	}

	return nil
}

func (l *MetadataLock) removeElementLock(lock distlock.Lock, locks []*metadataElementLock, reqID string) []*metadataElementLock {
	strTarget := lock.Target.(StringLockTarget)
	lck, index, ok := lo.FindIndexOf(locks, func(l *metadataElementLock) bool { return strTarget.IsConflict(&l.target) })
	if !ok {
		return locks
	}

	lck.requestIDs = lo2.Remove(lck.requestIDs, reqID)

	if len(lck.requestIDs) == 0 {
		locks = lo2.RemoveAt(locks, index)
	}

	return locks
}

// GetTargetString 将锁对象序列化为字符串，方便存储到ETCD
func (l *MetadataLock) GetTargetString(target any) (string, error) {
	tar := target.(StringLockTarget)
	return StringLockTargetToString(&tar)
}

// ParseTargetString 解析字符串格式的锁对象数据
func (l *MetadataLock) ParseTargetString(targetStr string) (any, error) {
	return StringLockTargetFromString(targetStr)
}

// Clear 清除内部所有状态
func (l *MetadataLock) Clear() {
	l.createReqIDs = nil
}
