package executor

import (
	"fmt"
	"github.com/33cn/chain33/client"
	dbm "github.com/33cn/chain33/common/db"
	"github.com/33cn/chain33/types"
	brokertypes "github.com/33cn/plugin/plugin/dapp/broker/types"
	"strconv"
)

//BrokerDB ...
type BrokerDB struct {
	api       client.QueueProtocolAPI
	statedb   dbm.KV
	localdb   dbm.KV
	txhash    []byte
	fromaddr  string
	blocktime int64
	height    int64
	index     int
}

var (
	// DefaultManagerAddr 默认管理员地址
	DefaultManagerAddr = "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
)

func newBrokerDB(b *broker, tx *types.Transaction, index int) *BrokerDB {
	hash := tx.Hash()
	fromaddr := tx.From()
	return &BrokerDB{b.GetAPI(), b.GetStateDB(), b.GetLocalDB(), hash, fromaddr,
		b.GetBlockTime(), b.GetHeight(), index}
}

//GetKVSet ...
func (b *BrokerDB) GetMetaKVSet(req uint64, meta *brokertypes.Meta) (kvset []*types.KeyValue) {
	if req == brokertypes.Req_Callback {
		kvset = append(kvset, &types.KeyValue{Key: calCallBackMetaKey(), Value: types.Encode(meta)})
	} else if req == brokertypes.Req_Inner {
		kvset = append(kvset, &types.KeyValue{Key: calInnerMetaKey(), Value: types.Encode(meta)})
	} else if req == brokertypes.Req_DstRollback {
		kvset = append(kvset, &types.KeyValue{Key: calDstRollBackMetaKey(), Value: types.Encode(meta)})
	}
	return kvset
}

//GetKVSet ...
func (b *BrokerDB) GetEventKVSet(event *brokertypes.InterchainEvent) (kvset []*types.KeyValue) {
	kvset = append(kvset, &types.KeyValue{Key: calServicePair(event.GetSrcServiceID(), event.GetDstServiceID(), event.Index), Value: types.Encode(event)})
	return kvset
}

//初始化broker
func (b *BrokerDB) initBroker(payload *brokertypes.Init) (*types.Receipt, error) {
	//权限检查且只能初始化一次
	if b.fromaddr != getManagerAddr(b.api.GetConfig(), b.statedb, brokerAdmin, DefaultManagerAddr) {
		return nil, fmt.Errorf("the addr %s is not admin", b.fromaddr)
	}
	m := make(map[string]uint64)
	//初始化map,填充一些默认值，防止序列化后为nil
	m["broker"] = 0
	meta := &brokertypes.Meta{
		Meta: m,
	}
	var kvsets []*types.KeyValue
	kvsets = append(kvsets, &types.KeyValue{Key: calOutterMetaKey(), Value: types.Encode(meta)})
	kvsets = append(kvsets, &types.KeyValue{Key: calInnerMetaKey(), Value: types.Encode(meta)})
	kvsets = append(kvsets, &types.KeyValue{Key: calCallBackMetaKey(), Value: types.Encode(meta)})
	kvsets = append(kvsets, &types.KeyValue{Key: calDstRollBackMetaKey(), Value: types.Encode(meta)})
	receipt := &types.Receipt{Ty: types.ExecOk}
	receipt.KV = append(receipt.KV, kvsets...)
	receipt.KV = append(receipt.KV, &types.KeyValue{Key: BxhIDKey(), Value: types.Encode(payload)})
	receipt.Ty = brokertypes.TyInitLog
	return receipt, nil
}

//其他执行器注册授权
func (b *BrokerDB) register(payload *brokertypes.RegisterExector) (*types.Receipt, error) {

	return nil, nil
}

//管理员审批
func (b *BrokerDB) audit(payload *brokertypes.Audit) (*types.Receipt, error) {

	return nil, nil
}

func (b *BrokerDB) updateIndex(payload *brokertypes.UpdateIndex) (*types.Receipt, error) {
	//FIXME 权限检查
	currentServiceID,err:= b.genFullServiceID(payload.DstServiceID)
	if err != nil {
		return nil, fmt.Errorf("can't get current service ID!")
	}
	receipt := &types.Receipt{Ty: types.ExecOk}
	if payload.ReqType == brokertypes.Req_Inner {
		inServicePair := genServicePair(payload.SrcServiceID, currentServiceID)
		meta, err := getMeta(b.statedb, calInnerMetaKey())
		if err != nil {
			return nil, fmt.Errorf("broker not init!")
		}
		payload.DstServiceID = currentServiceID
		idx := meta.Meta[inServicePair]
		if payload.SequenceNum != idx+1 {
			return nil, fmt.Errorf("Req_Inner incorrect index,key %s expect %d,but get %d",inServicePair, idx+1,payload.SequenceNum)
		}
		meta.Meta[inServicePair] = idx + 1
		kvset := b.GetMetaKVSet(payload.ReqType, meta)
		kvset = append(kvset, &types.KeyValue{Key: inMsgKey(inServicePair, idx+1), Value: types.Encode(payload.Response)})
		receipt.KV = append(receipt.KV, kvset...)

	} else if payload.ReqType == brokertypes.Req_Callback {
		outServicePair := genServicePair(currentServiceID, payload.GetSrcServiceID())
		meta, err := getMeta(b.statedb, calCallBackMetaKey())
		if err != nil {
			return nil, fmt.Errorf("broker not init!")
		}
		idx := meta.Meta[outServicePair]
		if payload.SequenceNum != idx+1 {
			return nil, fmt.Errorf("Req_Callback incorrect index,key %s expect %d,but get %d",outServicePair, idx+1,payload.SequenceNum)
		}
		meta.Meta[outServicePair] = idx + 1
		kvset := b.GetMetaKVSet(payload.ReqType, meta)
		// 跨出交易不需要确认是否成功
		receipt.KV = append(receipt.KV, kvset...)
	} else if payload.ReqType == brokertypes.Req_DstRollback {
		//todo
		inServicePair := genServicePair(payload.SrcServiceID, currentServiceID)
		meta, err := getMeta(b.statedb, calDstRollBackMetaKey())
		if err != nil {
			return nil, fmt.Errorf("broker not init!")
		}
		idx := meta.Meta[inServicePair]
		if payload.SequenceNum < idx+1 {
			return nil, fmt.Errorf("Req_DstRollback incorrect index,key %s expect %d,but get %d",inServicePair, idx+1,payload.SequenceNum)
		}
		meta.Meta[inServicePair] = payload.SequenceNum
		kvset := b.GetMetaKVSet(payload.ReqType, meta)
		receipt.KV = append(receipt.KV, kvset...)
	}
	log := &brokertypes.ReceiptBrokerLog{Value: &brokertypes.ReceiptBrokerLog_UpdateIndex{payload}, Ty: brokertypes.TyEmitInterchainEventAction}
	receiptlog := &types.ReceiptLog{Ty: brokertypes.TyUpdateIndexLog, Log: types.Encode(log)}
	receipt.Logs = append(receipt.Logs, receiptlog)
	return receipt, nil
}

//TODO 重构跨链事件发布,发布跨链事件，实质是从别的应用链获取数据
func (b *BrokerDB) emitInterchainEvent(payload *brokertypes.InterchainEvent) (*types.Receipt, error) {
	//FIXME 权限检查，是否需要设定指定账户拥有跨链的权限
	receipt := &types.Receipt{Ty: types.ExecOk}
	//发布跨链事件分为  1.信息跨链 2.代币跨链，代币跨链需要在这步将所需金额冻结在broker下面
	if payload.Type == brokertypes.Req_Type_Storage {
		//TODO 检查有没有历史数据
		meta, err := getMeta(b.statedb, calOutterMetaKey())
		if err != nil {
			return nil, fmt.Errorf("broker not init!")
		}
		currServiceID, err := b.getCurrServiceID()
		if err != nil {
			return nil, err
		}
		payload.SrcServiceID = currServiceID
		//idx :=meta.Meta[genServicePair(currServiceID, payload.DstServiceID)]
		meta.Meta[genServicePair(currServiceID, payload.DstServiceID)]++
		kvset := &types.KeyValue{Key: calOutterMetaKey(), Value: types.Encode(meta)}
		payload.Index = meta.Meta[genServicePair(currServiceID, payload.DstServiceID)]
		receipt.KV = append(receipt.KV, kvset)
		receipt.KV = append(receipt.KV, b.GetEventKVSet(payload)...)
		receipt.KV = append(receipt.KV, &types.KeyValue{Key: outMsgKey(genServicePair(payload.SrcServiceID, payload.DstServiceID), payload.Index), Value: types.Encode(payload)})
		log := &brokertypes.ReceiptBrokerLog{Value: &brokertypes.ReceiptBrokerLog_EmitInterchainEvent{payload}, Ty: brokertypes.TyEmitInterchainEventAction}
		receiptlog := &types.ReceiptLog{Ty: brokertypes.TyEmitInterchainEventLog, Log: types.Encode(log)}
		receipt.Logs = append(receipt.Logs, receiptlog)
	}

	if payload.Type == brokertypes.Req_Type_Coins {
		//TODO 跨链转账

	}
	return receipt, nil
}

//获取当前链得ID
func (b *BrokerDB) getCurrServiceID() (string, error) {
	return b.genFullServiceID(b.api.GetConfig().GetTitle() + delimiter + "broker")
}

func (b *BrokerDB) genFullServiceID(serviceId string) (string, error) {
	//TODO  需要init方法，用于初始化bixhID和appChainID
	data, err := b.statedb.Get(BxhIDKey())
	if err != nil {
		return "", err
	}
	var info brokertypes.BrokerInfo
	types.Decode(data, &info)
	return fmt.Sprintf("%s:%s:%s", info.GetBxhId(), info.GetAppChainId(), serviceId), nil
}

//获取broker信息
func getBrokerInfo(statedb dbm.KV) (*brokertypes.BrokerInfo, error) {
	//TODO  需要init方法，用于初始化bixhID和appChainID
	data, err := statedb.Get(BxhIDKey())
	if err != nil {
		return nil, err
	}
	var info brokertypes.BrokerInfo
	types.Decode(data, &info)
	return &info, nil
}

//去状态数据库中查询Event
func getEvent(statedb dbm.KV, key []byte) (*brokertypes.InterchainEvent, error) {
	data, err := statedb.Get(key)
	if err != nil {
		elog.Error("getEvent", "not found", "key:", key)
		return nil, err
	}
	var event brokertypes.InterchainEvent
	err = types.Decode(data, &event)
	if err != nil {
		elog.Error("getEvent", "can't decode", "err:", err.Error())
		return nil, err
	}
	return &event, nil
}

//去状态数据库中查询meta
func getMeta(statedb dbm.KV, key []byte) (*brokertypes.Meta, error) {
	data, err := statedb.Get(key)
	if err != nil {
		elog.Error("getMeta", "not found", "key:", string(key))
		return nil, err
	}
	var meta brokertypes.Meta
	err = types.Decode(data, &meta)
	if err != nil {
		elog.Error("getMeta", "can't decode", "err:", err.Error())
		return nil, err
	}
	return &meta, nil
}

//去状态数据库中查询message
func getMessage(statedb dbm.KV, key []byte) (*brokertypes.InterchainEvent, error) {
	data, err := statedb.Get(key)
	if err != nil {
		elog.Error("getMessage", "not found", "key:", string(key))
		return nil, err
	}
	var event brokertypes.InterchainEvent
	err = types.Decode(data, &event)
	if err != nil {
		elog.Error("getMessage", "can't decode", "err:", err.Error())
		return nil, err
	}
	return &event, nil
}

//监听获取跨出事件列表
func pollInterEvent(statedb dbm.KV, meta *brokertypes.Meta) (*brokertypes.InterChainEventList, error) {
	outMeta, err := getMeta(statedb, calOutterMetaKey())
	if err != nil {
		elog.Error("getMeta", "not found", "key:", calOutterMetaKey)
		return nil, err
	}
	var eventList brokertypes.InterChainEventList
	for method, idx := range outMeta.Meta {
		startPos, ok := meta.Meta[method]
		if !ok {
			startPos = 0
		}
		for i := startPos + 1; i <= idx; i++ {
			event, err := getEvent(statedb, calEventKey(method, i))
			if err != nil {
				elog.Error("get out event by key %s fail", calEventKey(method, i))
				continue
			}
			eventList.List = append(eventList.List, event)
		}
	}
	return &eventList, nil
}

//获取outerMeta
func getOutterMeta(statedb dbm.KV) (*brokertypes.Meta, error) {
	return getMeta(statedb, calOutterMetaKey())
}

//获取callBackMeta
func getCallBackMeta(statedb dbm.KV) (*brokertypes.Meta, error) {
	return getMeta(statedb, calCallBackMetaKey())
}

//获取innerMeta
func getInnerMeta(statedb dbm.KV) (*brokertypes.Meta, error) {
	return getMeta(statedb, calInnerMetaKey())
}

//获取message,用来查询跨入交易执行状态
func getInMessage(statedb dbm.KV, query *brokertypes.QueryInMessage) (*brokertypes.Response, error) {
	data, err := statedb.Get(inMsgKey(query.InServicePair, query.SequenceNum))
	if err != nil {
		elog.Error("getInMessage", "not found", "key:", inMsgKey(query.InServicePair, query.SequenceNum))
		return nil, err
	}
	var response brokertypes.Response
	err = types.Decode(data, &response)
	if err != nil {
		elog.Error("getInMessage", "can't decode", "err:", err.Error())
		return nil, err
	}
	return &response, nil
}

//获取message,实质用来获取跨出事件
func getOutMessage(statedb dbm.KV, query *brokertypes.QueryOutMessage) (*brokertypes.InterchainEvent, error) {
	return getMessage(statedb, outMsgKey(query.InServicePair, query.SequenceNum))
}

func getConfValue(cfg *types.Chain33Config, db dbm.KV, key string, defaultValue int64) int64 {
	var item types.ConfigItem
	value, err := getManageKey(cfg, key, db)
	if err != nil {
		return defaultValue
	}
	if value != nil {
		err = types.Decode(value, &item)
		if err != nil {
			elog.Debug("broker getConfValue", "decode db key:", key, "err", err.Error())
			return defaultValue
		}
	}
	values := item.GetArr().GetValue()
	if len(values) == 0 {
		elog.Debug("broker getConfValue", "can't get value from values arr. key:", key)
		return defaultValue
	}
	//取数组最后一位，作为最新配置项的值
	v, err := strconv.ParseInt(values[len(values)-1], 10, 64)
	if err != nil {
		elog.Debug("broker getConfValue", "Type conversion error:", err.Error())
		return defaultValue
	}
	return v
}
func getManagerAddr(cfg *types.Chain33Config, db dbm.KV, key, defaultValue string) string {
	var item types.ConfigItem
	value, err := getManageKey(cfg, key, db)
	if err != nil {
		return defaultValue
	}
	if value != nil {
		err = types.Decode(value, &item)
		if err != nil {
			elog.Debug("broker getConfValue", "decode db key:", key, "err", err.Error())
			return defaultValue
		}
	}
	values := item.GetArr().GetValue()
	if len(values) == 0 {
		elog.Debug("broker getConfValue", "can't get value from values arr. key:", key)
		return defaultValue
	}
	return values[len(values)-1]
}

func getManageKey(cfg *types.Chain33Config, key string, db dbm.KV) ([]byte, error) {
	manageKey := types.ManageKey(key)
	value, err := db.Get([]byte(manageKey))
	if err != nil {
		if cfg.IsPara() { //平行链只有一种存储方式
			elog.Debug("broker getManage", "can't get value from db,key:", key, "err", err.Error())
			return nil, err
		}
		elog.Debug("broker getManageKey", "get db key", "not found")
		return getConfigKey(key, db)
	}
	return value, nil
}

func getConfigKey(key string, db dbm.KV) ([]byte, error) {
	configKey := types.ConfigKey(key)
	value, err := db.Get([]byte(configKey))
	if err != nil {
		elog.Debug("broker getConfigKey", "can't get value from db,key:", key, "err", err.Error())
		return nil, err
	}
	return value, nil
}
