/*Package commands implement dapp client commands*/
package commands

import (
	"encoding/json"
	"fmt"
	"github.com/33cn/chain33/rpc/jsonclient"
	rpctypes "github.com/33cn/chain33/rpc/types"
	"github.com/33cn/chain33/types"
	brokerty "github.com/33cn/plugin/plugin/dapp/broker/types"
	storgety "github.com/33cn/plugin/plugin/dapp/storage/types"
	. "github.com/bitly/go-simplejson"
	"github.com/spf13/cobra"
)

/*
 * 实现合约对应客户端
 */

// Cmd broker client command
func Cmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "broker",
		Short: "broker command",
		Args:  cobra.MinimumNArgs(1),
	}
	cmd.AddCommand(
		//add sub command
		InterchainGetRawTxCmd(),
		InterchainSetRawTxCmd(),
		Init_Broker(),
		QueryStorageCmd(),
	)
	return cmd
}

// InterchainGetRawTxCmd 发布信息交换跨链事件
func InterchainGetRawTxCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "interchainGet",
		Short: "publish a new event",
		Run:   emitDataSwapInterchainEvent,
	}
	InterchainGetFlags(cmd)
	return cmd
}
func InterchainGetFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("dstServiceID", "d", "", "dst service id, such as \"link33:fabric:mychannel&data_swapper\"")
	err := cmd.MarkFlagRequired("dstServiceID")
	if err != nil {
		fmt.Printf("MarkFlagRequired dstServiceID Error: %v", err)
		return
	}

	cmd.Flags().StringP("args", "a", "", "func args, such as \"key\"")
	err = cmd.MarkFlagRequired("args")
	if err != nil {
		fmt.Printf("MarkFlagRequired args Error: %v", err)
		return
	}

	cmd.Flags().StringP("argscb", "c", "", "callback func  args, such as \"key\"")
	err = cmd.MarkFlagRequired("argscb")
	if err != nil {
		fmt.Printf("MarkFlagRequired argscb  Error: %v", err)
		return
	}

	cmd.Flags().StringP("argsrb", "r", "", "rollback func args")
	err = cmd.MarkFlagRequired("argsrb")
	if err != nil {
		fmt.Printf("MarkFlagRequired argsrb Error: %v", err)
		return
	}

	cmd.Flags().StringP("func", "f", "", " func,callback,rollback")
	err = cmd.MarkFlagRequired("func")
	if err != nil {
		fmt.Printf("MarkFlagRequired func Error: %v", err)
		return
	}
}

func emitDataSwapInterchainEvent(cmd *cobra.Command, args []string) {
	paraName, _ := cmd.Flags().GetString("paraName")
	rpcLaddr, err := cmd.Flags().GetString("rpc_laddr")
	if err != nil {
		fmt.Printf("publishEvent get rpc addr Error: %v", err)
		return
	}
	dstServiceID, err := cmd.Flags().GetString("dstServiceID")
	if err != nil {
		fmt.Printf("publishEvent get dstServiceID Error: %v", err)
		return
	}
	arg, err := cmd.Flags().GetString("args")
	if err != nil {
		fmt.Printf("publishEvent get args Error: %v", err)
		return
	}

	argscb, err := cmd.Flags().GetString("argscb")
	if err != nil {
		fmt.Printf("publishEvent get argscb Error: %v", err)
		return
	}
	argsrb, err := cmd.Flags().GetString("argsrb")
	if err != nil {
		fmt.Printf("publishEvent get argsrb Error: %v", err)
		return
	}

	funcName, err := cmd.Flags().GetString("func")
	if err != nil {
		fmt.Printf("publishEvent get funcName Error: %v", err)
		return
	}
	params := &rpctypes.CreateTxIn{
		Execer:     types.GetExecName(brokerty.BrokerX, paraName),
		ActionName: brokerty.NameEmitInterchainEventAction,
		Payload:    []byte(fmt.Sprintf("{\"dstServiceID\":\"%s\",\"func\":\"%s\",\"args\":\"%s\",\"argscb\":\"%s\", \"argsrb\":\"%s\"}", dstServiceID, funcName, arg, argscb, argsrb)),
	}

	var res string
	ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
	ctx.RunWithoutMarshal()
}

// InterchainSet 发布信息交换跨链事件
func InterchainSetRawTxCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "interchainSet",
		Short: "storage the key value",
		Run:   interchainSet,
	}
	InterchainSetFlags(cmd)
	return cmd
}
func InterchainSetFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("key", "k", "", "storage Kv, such as \"key\"")
	err := cmd.MarkFlagRequired("key")
	if err != nil {
		fmt.Printf("MarkFlagRequired key Error: %v", err)
		return
	}

	cmd.Flags().StringP("value", "v", "", "value of key")
	err = cmd.MarkFlagRequired("value")
	if err != nil {
		fmt.Printf("MarkFlagRequired value Error: %v", err)
		return
	}

}

func interchainSet(cmd *cobra.Command, args []string) {
	paraName, _ := cmd.Flags().GetString("paraName")
	rpcLaddr, err := cmd.Flags().GetString("rpc_laddr")
	if err != nil {
		fmt.Printf("publishEvent get rpc addr Error: %v", err)
		return
	}
	key, err := cmd.Flags().GetString("key")
	if err != nil {
		fmt.Printf("publishEvent get key Error: %v", err)
		return
	}
	value, err := cmd.Flags().GetString("value")
	if err != nil {
		fmt.Printf("publishEvent get value Error: %v", err)
		return
	}
	params := &rpctypes.CreateTxIn{
		Execer:     types.GetExecName(storgety.StorageX, paraName),
		ActionName: storgety.NameContentStorageAction,
		Payload:    []byte(fmt.Sprintf("{\"key\":\"%s\",\"value\":\"%s\"}", key, value)),
	}
	var res string
	ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
	ctx.RunWithoutMarshal()
}

//初始化broker合约
func Init_Broker() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "init_broker",
		Short: "init broker contract",
		Run:   init_broker,
	}
	initBrokerFlags(cmd)
	return cmd
}
func initBrokerFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("bxhID", "b", "link33", "link33 version, such as \"link33\"")
	err := cmd.MarkFlagRequired("bxhID")
	if err != nil {
		fmt.Printf("MarkFlagRequired bxhID Error: %v", err)
		return
	}

	cmd.Flags().StringP("appChainID", "a", "chain33", "appChainID, such as \"chain33\"")
	err = cmd.MarkFlagRequired("appChainID")
	if err != nil {
		fmt.Printf("MarkFlagRequired appChainID Error: %v", err)
		return
	}
}

func init_broker(cmd *cobra.Command, args []string) {
	paraName, _ := cmd.Flags().GetString("paraName")
	rpcLaddr, err := cmd.Flags().GetString("rpc_laddr")
	if err != nil {
		fmt.Printf("Init_Broker get rpc addr Error: %v", err)
		return
	}
	bxhID, err := cmd.Flags().GetString("bxhID")
	if err != nil {
		fmt.Printf("Init_Broker get bxhID Error: %v", err)
		return
	}
	appChainID, err := cmd.Flags().GetString("appChainID")
	if err != nil {
		fmt.Printf("Init_Broker get appChainID Error: %v", err)
		return
	}
	params := &rpctypes.CreateTxIn{
		Execer:     types.GetExecName(brokerty.BrokerX, paraName),
		ActionName: brokerty.NameInitAction,
		Payload:    []byte(fmt.Sprintf("{\"bxhId\":\"%s\",\"appChainId\":\"%s\"}", bxhID, appChainID)),
	}
	var res string
	ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
	ctx.RunWithoutMarshal()
}

func QueryStorageByKey(prefix, rpcLaddr, key string) (*storgety.Storage, error) {
	params := &rpctypes.Query4Jrpc{
		Execer:   types.GetExecName(storgety.StorageX, prefix),
		FuncName: storgety.FuncNameQueryStorage,
		Payload:  []byte(fmt.Sprintf("{\"txHash\":\"%s\"}", key)),
	}
	var res json.RawMessage
	ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
	ctx.SetResultCb(ParseStorage)
	data, err := ctx.RunResult()
	if err != nil {
		return nil, err
	}
	return data.(*storgety.Storage), nil
}

func QueryStorageCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "query",
		Short: "Query storage by key",
		Run:   getStorage,
	}
	GetStorageFlags(cmd)
	return cmd
}
func GetStorageFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("key", "k", "", "get storage key, such as \"key\"")
	err := cmd.MarkFlagRequired("key")
	if err != nil {
		fmt.Printf("MarkFlagRequired get key Error: %v", err)
		return
	}
}

func getStorage(cmd *cobra.Command, args []string) {
	paraName, _ := cmd.Flags().GetString("paraName")
	rpcLaddr, err := cmd.Flags().GetString("rpc_laddr")
	if err != nil {
		fmt.Printf("getStorage get rpc addr Error: %v", err)
		return
	}
	key, err := cmd.Flags().GetString("key")
	if err != nil {
		fmt.Printf("getStorage get dstServiceID Error: %v", err)
		return
	}

	storage, err := QueryStorageByKey(paraName, rpcLaddr, key)
	if err != nil {
		fmt.Println("QueryStorageBykey Error: %v", err)
		return
	}
	fmt.Println(storage.GetContentStorage().GetValue())
	return

}

// 回调解析函数
func ParseStorage(res interface{}) (interface{}, error) {
	raw, ok := res.(*json.RawMessage)
	if !ok {
		return nil, fmt.Errorf("res is not json rawMessage type!")
	}
	js, err := NewJson(*raw)
	if err != nil {
		return nil, err
	}
	if contentStorge, ok := js.CheckGet("contentStorage"); ok {
		value, _ := contentStorge.Get("value").String()
		key, _ := contentStorge.Get("key").String()
		op, _ := contentStorge.Get("op").Int()
		storage := &storgety.Storage{Ty: storgety.TyContentStorageAction, Value: &storgety.Storage_ContentStorage{ContentStorage: &storgety.ContentOnlyNotaryStorage{
			Content: nil,
			Key:     key,
			Op:      int32(op),
			Value:   value,
		}}}
		return storage, nil
	}
	return nil, fmt.Errorf("unknow storage type!")
}
