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[*BypassToShardStore]()
	exec.UseOp[*BypassToPublicStore]()

	exec.UseVarValue[*BypassedFileInfoValue]()
	exec.UseVarValue[*BypassHandleResultValue]()

	exec.UseOp[*BypassFromShardStore]()
	exec.UseOp[*BypassFromPublicStore]()
	exec.UseVarValue[*BypassFilePathValue]()

	exec.UseOp[*BypassFromShardStoreHTTP]()
	exec.UseVarValue[*HTTPRequestValue]()
}

type BypassedFileInfoValue struct {
	types.BypassedFileInfo
}

func (v *BypassedFileInfoValue) Clone() exec.VarValue {
	return &BypassedFileInfoValue{
		BypassedFileInfo: v.BypassedFileInfo,
	}
}

type BypassHandleResultValue struct {
	Commited bool
}

func (r *BypassHandleResultValue) Clone() exec.VarValue {
	return &BypassHandleResultValue{
		Commited: r.Commited,
	}
}

type BypassToShardStore struct {
	UserSpace      clitypes.UserSpaceDetail
	BypassFileInfo exec.VarID
	BypassCallback exec.VarID
	FileInfo       exec.VarID
}

func (o *BypassToShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return err
	}

	shardStore, err := stgPool.GetShardStore(&o.UserSpace)
	if err != nil {
		return err
	}

	br, ok := shardStore.(types.BypassShardWrite)
	if !ok {
		return fmt.Errorf("shard store %v not support bypass write", o.UserSpace)
	}

	fileInfo, err := exec.BindVar[*BypassedFileInfoValue](e, ctx.Context, o.BypassFileInfo)
	if err != nil {
		return err
	}

	err = br.BypassedShard(fileInfo.BypassedFileInfo)
	if err != nil {
		return err
	}

	e.PutVar(o.BypassCallback, &BypassHandleResultValue{Commited: true})
	e.PutVar(o.FileInfo, &ShardInfoValue{Hash: fileInfo.Hash, Size: fileInfo.Size})
	return nil
}

func (o *BypassToShardStore) String() string {
	return fmt.Sprintf("BypassToShardStore[UserSpace:%v] Info: %v, Callback: %v", o.UserSpace, o.BypassFileInfo, o.BypassCallback)
}

type BypassToPublicStore struct {
	UserSpace      clitypes.UserSpaceDetail
	BypassFileInfo exec.VarID
	BypassCallback exec.VarID
	DestPath       string
}

func (o *BypassToPublicStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return err
	}

	store, err := stgPool.GetPublicStore(&o.UserSpace)
	if err != nil {
		return err
	}

	br, ok := store.(types.BypassPublicWrite)
	if !ok {
		return fmt.Errorf("public store %v not support bypass write", o.UserSpace)
	}

	fileInfo, err := exec.BindVar[*BypassedFileInfoValue](e, ctx.Context, o.BypassFileInfo)
	if err != nil {
		return err
	}

	err = br.BypassedPublic(fileInfo.BypassedFileInfo, o.DestPath)
	if err != nil {
		return err
	}

	e.PutVar(o.BypassCallback, &BypassHandleResultValue{Commited: true})
	return nil
}

func (o *BypassToPublicStore) String() string {
	return fmt.Sprintf("BypassToPublicStore[UserSpace:%v] Info: %v, Callback: %v", o.UserSpace, o.BypassFileInfo, o.BypassCallback)
}

type BypassFilePathValue struct {
	types.BypassFilePath
}

func (v *BypassFilePathValue) Clone() exec.VarValue {
	return &BypassFilePathValue{
		BypassFilePath: v.BypassFilePath,
	}
}

type BypassFromShardStore struct {
	UserSpace clitypes.UserSpaceDetail
	FileHash  clitypes.FileHash
	Output    exec.VarID
}

func (o *BypassFromShardStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return err
	}

	shardStore, err := stgPool.GetShardStore(&o.UserSpace)
	if err != nil {
		return err
	}

	br, ok := shardStore.(types.BypassShardRead)
	if !ok {
		return fmt.Errorf("shard store %v not support bypass read", o.UserSpace)
	}

	path, err := br.BypassShardRead(o.FileHash)
	if err != nil {
		return err
	}

	e.PutVar(o.Output, &BypassFilePathValue{BypassFilePath: path})
	return nil
}

func (o *BypassFromShardStore) String() string {
	return fmt.Sprintf("BypassFromShardStore[UserSpace:%v] FileHash: %v, Output: %v", o.UserSpace, o.FileHash, o.Output)
}

type BypassFromPublicStore struct {
	UserSpace clitypes.UserSpaceDetail
	Path      string
	Output    exec.VarID
}

func (o *BypassFromPublicStore) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return err
	}

	store, err := stgPool.GetPublicStore(&o.UserSpace)
	if err != nil {
		return err
	}

	br, ok := store.(types.BypassPublicRead)
	if !ok {
		return fmt.Errorf("public store %v not support bypass read", o.UserSpace)
	}

	path, err := br.BypassPublicRead(o.Path)
	if err != nil {
		return err
	}

	e.PutVar(o.Output, &BypassFilePathValue{BypassFilePath: path})
	return nil
}

func (o *BypassFromPublicStore) String() string {
	return fmt.Sprintf("BypassFromPublicStore[UserSpace:%v] Path: %v, Output: %v", o.UserSpace, o.Path, o.Output)
}

// 旁路Http读取
type BypassFromShardStoreHTTP struct {
	UserSpace clitypes.UserSpaceDetail
	FileHash  clitypes.FileHash
	Output    exec.VarID
}

type HTTPRequestValue struct {
	types.HTTPRequest
}

func (v *HTTPRequestValue) Clone() exec.VarValue {
	return &HTTPRequestValue{
		HTTPRequest: v.HTTPRequest,
	}
}

func (o *BypassFromShardStoreHTTP) Execute(ctx *exec.ExecContext, e *exec.Executor) error {
	stgPool, err := exec.GetValueByType[*pool.Pool](ctx)
	if err != nil {
		return err
	}

	shardStore, err := stgPool.GetShardStore(&o.UserSpace)
	if err != nil {
		return err
	}

	br, ok := shardStore.(types.HTTPBypassShardRead)
	if !ok {
		return fmt.Errorf("shard store %v not support bypass read", o.UserSpace)
	}

	req, err := br.HTTPBypassRead(o.FileHash)
	if err != nil {
		return err
	}

	e.PutVar(o.Output, &HTTPRequestValue{HTTPRequest: req})
	return nil
}

func (o *BypassFromShardStoreHTTP) String() string {
	return fmt.Sprintf("BypassFromShardStoreHTTP[UserSpace:%v] FileHash: %v, Output: %v", o.UserSpace, o.FileHash, o.Output)
}

// 旁路写入
type BypassToShardStoreNode struct {
	dag.NodeBase
	UserSpace        clitypes.UserSpaceDetail
	FileHashStoreKey string
}

func (b *GraphNodeBuilder) NewBypassToShardStore(userSpace clitypes.UserSpaceDetail, fileHashStoreKey string) *BypassToShardStoreNode {
	node := &BypassToShardStoreNode{
		UserSpace:        userSpace,
		FileHashStoreKey: fileHashStoreKey,
	}
	b.AddNode(node)

	node.InputValues().Init(1)
	node.OutputValues().Init(node, 2)
	return node
}

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

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

func (n *BypassToShardStoreNode) FileHashVar() dag.ValueOutputSlot {
	return dag.ValueOutputSlot{
		Node:  n,
		Index: 1,
	}
}

func (t *BypassToShardStoreNode) GenerateOp() (exec.Op, error) {
	return &BypassToShardStore{
		UserSpace:      t.UserSpace,
		BypassFileInfo: t.BypassFileInfoSlot().Var().VarID,
		BypassCallback: t.BypassCallbackVar().Var().VarID,
		FileInfo:       t.FileHashVar().Var().VarID,
	}, nil
}

type BypassToPublicStoreNode struct {
	dag.NodeBase
	UserSpace clitypes.UserSpaceDetail
	DestPath  string
}

func (b *GraphNodeBuilder) NewBypassToPublicStore(userSpace clitypes.UserSpaceDetail, dstPath string) *BypassToPublicStoreNode {
	node := &BypassToPublicStoreNode{
		UserSpace: userSpace,
		DestPath:  dstPath,
	}
	b.AddNode(node)

	node.InputValues().Init(1)
	node.OutputValues().Init(node, 1)
	return node
}

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

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

func (t *BypassToPublicStoreNode) GenerateOp() (exec.Op, error) {
	return &BypassToPublicStore{
		UserSpace:      t.UserSpace,
		BypassFileInfo: t.BypassFileInfoSlot().Var().VarID,
		BypassCallback: t.BypassCallbackVar().Var().VarID,
		DestPath:       t.DestPath,
	}, nil
}

// 旁路读取
type BypassFromShardStoreNode struct {
	dag.NodeBase
	UserSpace clitypes.UserSpaceDetail
	FileHash  clitypes.FileHash
}

func (b *GraphNodeBuilder) NewBypassFromShardStore(userSpace clitypes.UserSpaceDetail, fileHash clitypes.FileHash) *BypassFromShardStoreNode {
	node := &BypassFromShardStoreNode{
		UserSpace: userSpace,
		FileHash:  fileHash,
	}
	b.AddNode(node)

	node.OutputValues().Init(node, 1)
	return node
}

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

func (n *BypassFromShardStoreNode) GenerateOp() (exec.Op, error) {
	return &BypassFromShardStore{
		UserSpace: n.UserSpace,
		FileHash:  n.FileHash,
		Output:    n.FilePathVar().Var().VarID,
	}, nil
}

type BypassFromPublicStoreNode struct {
	dag.NodeBase
	UserSpace clitypes.UserSpaceDetail
	Path      string
}

func (b *GraphNodeBuilder) NewBypassFromPublicStore(userSpace clitypes.UserSpaceDetail, path string) *BypassFromPublicStoreNode {
	node := &BypassFromPublicStoreNode{
		UserSpace: userSpace,
		Path:      path,
	}
	b.AddNode(node)

	node.OutputValues().Init(node, 1)
	return node
}

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

func (n *BypassFromPublicStoreNode) GenerateOp() (exec.Op, error) {
	return &BypassFromPublicStore{
		UserSpace: n.UserSpace,
		Path:      n.Path,
		Output:    n.FilePathVar().Var().VarID,
	}, nil
}

// 旁路Http读取
type BypassFromShardStoreHTTPNode struct {
	dag.NodeBase
	UserSpace clitypes.UserSpaceDetail
	FileHash  clitypes.FileHash
}

func (b *GraphNodeBuilder) NewBypassFromShardStoreHTTP(userSpace clitypes.UserSpaceDetail, fileHash clitypes.FileHash) *BypassFromShardStoreHTTPNode {
	node := &BypassFromShardStoreHTTPNode{
		UserSpace: userSpace,
		FileHash:  fileHash,
	}
	b.AddNode(node)

	node.OutputValues().Init(node, 1)
	return node
}

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

func (n *BypassFromShardStoreHTTPNode) GenerateOp() (exec.Op, error) {
	return &BypassFromShardStoreHTTP{
		UserSpace: n.UserSpace,
		FileHash:  n.FileHash,
		Output:    n.HTTPRequestVar().Var().VarID,
	}, nil
}
