package ops2

import (
	"fmt"

	"gitlink.org.cn/cloudream/common/pkgs/ioswitch/dag"
	"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
	clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
)

func init() {
	exec.UseOp[*S2STransfer]()
}

type S2STransfer struct {
	Src            clitypes.UserSpaceDetail
	SrcPath        exec.VarID
	Dst            clitypes.UserSpaceDetail
	Output         exec.VarID
	BypassCallback exec.VarID
	S2SOption      types.S2SOption
}

func (o *S2STransfer) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	srcPath, err := exec.BindVar[*BypassFilePathValue](e, ctx.Context, o.SrcPath)
	if err != nil {
		return err
	}

	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return fmt.Errorf("getting storage pool: %w", err)
	}

	s2s, err := stgPool.GetS2STransfer(&o.Dst)
	if err != nil {
		return err
	}

	// 传输文件
	dstPath, err := s2s.Transfer(ctx.Context, &o.Src, srcPath.Path, o.S2SOption)
	if err != nil {
		return err
	}
	defer s2s.Abort()

	// 告知后续Op处理临时文件
	e.PutVar(o.Output, &BypassedFileInfoValue{BypassedFileInfo: types.BypassedFileInfo{
		Path: dstPath,
		Hash: srcPath.Info.Hash,
		Size: srcPath.Info.Size,
	}})

	// 等待后续Op处理临时文件
	cb, err := exec.BindVar[*BypassHandleResultValue](e, ctx.Context, o.BypassCallback)
	if err != nil {
		return fmt.Errorf("getting temp file callback: %v", err)
	}

	if cb.Commited {
		s2s.Complete()
	}

	return nil
}

func (o *S2STransfer) String() string {
	return fmt.Sprintf("S2STransfer %v:%v -> %v:%v, Callback: %v", o.Src.Storage.String(), o.SrcPath, o.Dst.Storage.String(), o.Output, o.BypassCallback)
}

type S2STransferNode struct {
	dag.NodeBase
	Src clitypes.UserSpaceDetail
	Dst clitypes.UserSpaceDetail
	Opt types.S2SOption
}

func (b *GraphNodeBuilder) NewS2STransfer(src, dst clitypes.UserSpaceDetail, opt types.S2SOption) *S2STransferNode {
	n := &S2STransferNode{
		Src: src,
		Dst: dst,
		Opt: opt,
	}
	b.AddNode(n)

	n.OutputValues().Init(n, 1)
	n.InputValues().Init(2)

	return n
}

func (n *S2STransferNode) SrcPathSlot() dag.ValueInputSlot {
	return dag.ValueInputSlot{
		Node:  n,
		Index: 0,
	}
}

func (n *S2STransferNode) BypassCallbackSlot() dag.ValueInputSlot {
	return dag.ValueInputSlot{
		Node:  n,
		Index: 1,
	}
}

func (n *S2STransferNode) BypassFileInfoVar() dag.ValueOutputSlot {
	return dag.ValueOutputSlot{
		Node:  n,
		Index: 0,
	}
}

func (n *S2STransferNode) GenerateOp() (exec.Op, error) {
	return &S2STransfer{
		Src:            n.Src,
		SrcPath:        n.SrcPathSlot().Var().VarID,
		Dst:            n.Dst,
		Output:         n.BypassFileInfoVar().Var().VarID,
		BypassCallback: n.BypassCallbackSlot().Var().VarID,
		S2SOption:      n.Opt,
	}, nil
}
