package ticktock

import (
	"context"
	"fmt"
	"time"

	"gitlink.org.cn/cloudream/common/pkgs/logger"
	"gitlink.org.cn/cloudream/common/utils/reflect2"
	"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
	"gitlink.org.cn/cloudream/jcs-pub/client/types"
	stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
	cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"

	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/distlock/reqbuilder"
	hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
)

type ShardStoreGC struct {
}

func (j *ShardStoreGC) Name() string {
	return reflect2.TypeNameOf[ShardStoreGC]()
}

// Execute 执行垃圾回收操作。
func (j *ShardStoreGC) Execute(t *TickTock) {
	log := logger.WithType[ShardStoreGC]("Event")
	startTime := time.Now()
	log.Debugf("job start")
	defer func() {
		log.Debugf("job end, time: %v", time.Since(startTime))
	}()

	spaceIDs, err := t.db.UserSpace().GetAllIDs(t.db.DefCtx())
	if err != nil {
		log.Warnf("getting user space ids: %v", err)
		return
	}

	for _, spaceID := range spaceIDs {
		detail := t.spaceMeta.Get(spaceID)
		if detail == nil {
			continue
		}

		err := j.gcOne(t, detail)
		if err != nil {
			log.Warnf("gc one user space: %v: %v", spaceID, err)
			continue
		}
	}
}

func (j *ShardStoreGC) gcOne(t *TickTock, space *types.UserSpaceDetail) error {
	mutex, err := reqbuilder.NewBuilder().Shard().GC(space.UserSpace.UserSpaceID).MutexLock(t.pubLock)
	if err != nil {
		return fmt.Errorf("acquire lock: %w", err)
	}
	defer mutex.Unlock()

	db2 := t.db

	// 收集需要进行垃圾回收的文件哈希值
	var allFileHashes []types.FileHash
	err = db2.DoTx(func(tx db.SQLContext) error {
		blocks, err := db2.ObjectBlock().GetByUserSpaceID(tx, space.UserSpace.UserSpaceID)
		if err != nil {
			return fmt.Errorf("getting object blocks by hub id: %w", err)
		}
		for _, c := range blocks {
			allFileHashes = append(allFileHashes, c.FileHash)
		}

		objs, err := db2.PinnedObject().GetObjectsByUserSpaceID(tx, space.UserSpace.UserSpaceID)
		if err != nil {
			return fmt.Errorf("getting pinned objects by hub id: %w", err)
		}
		for _, o := range objs {
			allFileHashes = append(allFileHashes, o.FileHash)
		}

		return nil
	})
	if err != nil {
		return err
	}

	// 获取与节点通信的代理客户端
	addr, ok := space.MasterHub.Address.(*cortypes.GRPCAddressInfo)
	if !ok {
		return fmt.Errorf("master of user space %v has no grpc address", space.UserSpace)
	}
	agtCli := stgglb.HubRPCPool.Get(stgglb.SelectGRPCAddress(*space.MasterHub, *addr))
	defer agtCli.Release()

	// 向代理发送垃圾回收请求
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
	defer cancel()

	_, cerr := agtCli.CacheGC(ctx, &hubrpc.CacheGC{
		UserSpace:  *space,
		Availables: allFileHashes,
	})
	if cerr != nil {
		return fmt.Errorf("request to cache gc: %w", cerr)
	}
	return nil
}
