package main

import (
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"math/big"
	"reflect"
)

func transactionProcess(paths, args []string, stub shim.ChaincodeStubInterface) pb.Response {
	switch paths[0] {
	case "issue": //进行账户金额发行（无中生有）
		return issueApi(args, stub)
	case "extract": //提取账户金额（直接减去）
		return extractApi(args, stub)
	case "transfer": // 一笔普通转账
		return transferApi(args, stub)
	default:
		return shim.Error(fmt.Sprintf("Unsupported schema function of %s", paths[0]))
	}
}

func issueApi(args []string, stub shim.ChaincodeStubInterface) pb.Response {
	if len(args) != 1 {
		return shim.Error("issue operation expected 1 parameters ! ")
	}
	action := &Action{}
	err := json.Unmarshal([]byte(args[0]), action)
	if err != nil {
		return shim.Error(fmt.Sprintf("json unmarshal Action structure fail,err: %s", err))
	}
	res, err := funtOperation(action, stub, true)
	if err != nil {
		return shim.Error(fmt.Sprintf("issue operation err: %s", err))
	}
	return shim.Success([]byte("extract " + res))
}

func extractApi(args []string, stub shim.ChaincodeStubInterface) pb.Response {
	if len(args) != 1 {
		return shim.Error("extract  operation expected 1 parameters ! ")
	}
	action := &Action{}
	err := json.Unmarshal([]byte(args[0]), action)
	if err != nil {
		return shim.Error(fmt.Sprintf("json unmarshal Action structure fail,err: %s", err))
	}
	res, err := funtOperation(action, stub, false)
	if err != nil {
		return shim.Error(fmt.Sprintf("extract operation err: %s", err))
	}
	return shim.Success([]byte("extract " + res))
}

func transferApi(args []string, stub shim.ChaincodeStubInterface) pb.Response {
	txFunc := transferType[args[0]]
	if txFunc == nil {
		return shim.Error(fmt.Sprintf("No matching transfer operation method was found ! "))
	}
	err := txFunc(args[1:], stub)
	if err != nil {
		return shim.Error(fmt.Sprintf("%s transfer operation fail,err:%s ", args[0], err))
	}
	return pb.Response{}
}

/*
	账户金额发现，核心是在对应的schema 找到有发行的权利。
	1：金额字段也就是（账本字段数据）都有发行标示
	2：发行交易可以存在多个字段，但只要保证我们要求的字段存在就可以。账户类型，Id，发行金额（大于0）
*/
func funtOperation(action *Action, stub shim.ChaincodeStubInterface, isIssue bool) (string, error) {

	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}

	schema := &Schema{}
	schema, err = getSchema(action.AccountType, stub)
	if err != nil {
		return "", err
	}
	leadgerKey := LeadgerPrefix + "_" + action.AccountType + "_" + action.AccountId
	leadgermap, err := getState(leadgerKey, stub)
	if err != nil {
		return "", err
	}
	onwer, ok := leadgermap["common_name"].(string)
	if isIssue {
		err := schema.AuthorityCheck(schema.LeadgerFields[action.FundType].PAuth.Issue, commonName, onwer)
		if err != nil {
			return "", fmt.Errorf("%s field issue permission check does not match, err: %s ", action.FundType, err)
		}
	} else {
		err := schema.AuthorityCheck(schema.LeadgerFields[action.FundType].PAuth.Extract, commonName, onwer)
		if err != nil {
			return "", fmt.Errorf("%s field extract permission check does not match, err: %s ", action.FundType, err)
		}
	}
	funt, ok := leadgermap[action.FundType].(string)
	funtAmount, ok := new(big.Int).SetString(funt, 10)
	if !ok {
		return "", fmt.Errorf("%s parameter resolution to big.int failed ", action.FundType)
	}
	actionAmount, ok := new(big.Int).SetString(action.ActionAmount, 10)
	if !ok {
		return "", fmt.Errorf("action_amount parameter resolution to big.int failed ")
	}
	sum := new(big.Int)
	if isIssue {
		sum = sum.Add(funtAmount, actionAmount)
	} else {
		if funtAmount.Cmp(actionAmount) == -1 {
			return "", fmt.Errorf("The current amount is less than the withdrawal amount and cannot be extracted ")
		}
		sum = sum.Sub(funtAmount, actionAmount)
	}
	leadgermap[action.FundType] = sum.String()
	leadgerResult, _ := json.Marshal(leadgermap)

	err = stub.PutState(leadgerKey, leadgerResult)
	if err != nil {
		return "", fmt.Errorf("putState %s data fail,err: %s", leadgerKey, err)
	}
	return "transaction successful!", nil
}

/*

 */
func normalTransfer(args []string, stub shim.ChaincodeStubInterface) error {
	if len(args) != 1 {
		return nil
	}
	tx := &CommonTx{}
	err := json.Unmarshal([]byte(args[0]), tx)
	if err != nil {
		fmt.Errorf("CommonTx structure json unmarshal fail,err : %s ", err)
	}
	sender := &TransferAccount{
		tx.SenderAccountType,
		tx.SenderId,
		tx.SenderFunt,
	}
	receiver := &TransferAccount{
		tx.ReceiverAccountType,
		tx.ReceiverId,
		tx.ReceiverFunt,
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return nil
	}
	amount, ok := new(big.Int).SetString(tx.Amount, 10)
	if !ok {
		return fmt.Errorf("amount parameter resolution to big.int failed ")
	}
	return transfer(amount, sender, receiver, commonName, stub)
}

/*
	当增加一条交易记录的时候，我们需要根据交易记录实现，
1.预付金的计算，2.计算分成，3.预付金的变化。分成的计算
*/
func merchantsale(args []string, stub shim.ChaincodeStubInterface) error {
	commanName, err := getCertificateCommonName(stub)
	if err != nil {
		return err
	}
	senderaccountType := "merchants"
	receiverAccountType := "alibusi"
	mer := &Merchantsale{}
	err = json.Unmarshal([]byte(args[0]), mer)
	if err != nil {
		return fmt.Errorf("Merchantsale struct json unmarshal fail,err : %s ", err)
	}
	sender := &TransferAccount{
		senderaccountType,
		mer.Mid,
		"regular",
	}
	receiver := &TransferAccount{
		receiverAccountType,
		mer.Bid,
		"abail",
	}

	rtreposit, ok := new(big.Int).SetString(mer.Treposit, 10)
	if !ok {
		return fmt.Errorf("Treposit parameter resolution to big.int failed ")
	}
	//errinfo := make(chan error)
	err = transfer(rtreposit, sender, receiver, commanName, stub)
	if err != nil {
		return err
	}
	//1.计算预付金 这部分待定
	treposit := new(big.Int)
	nodisamount, ok := new(big.Int).SetString(mer.Nodisamount, 10)
	if !ok {
		return fmt.Errorf("Nodisamount parameter resolution to big.int failed ")
	}
	disamount, ok := new(big.Int).SetString(mer.Disamount, 10)
	if !ok {
		return fmt.Errorf("Disamount parameter resolution to big.int failed ")
	}
	oamount, ok := new(big.Int).SetString(mer.Oamount, 10)
	if !ok {
		return fmt.Errorf("Oamount parameter resolution to big.int failed ")
	}
	if nodisamount.Cmp(big.NewInt(0)) == 0 || disamount.Cmp(big.NewInt(0)) == 0 { //如果打折金额与不打折金额任一为0，则认为全部打折
		treposit, err = dividecalc(oamount, mer.Adiscounts)
		if err != nil {
			return err
		}
	} else {
		distre, err := dividecalc(disamount, mer.Adiscounts) //计算打折部分的金额
		if err != nil {
			return err
		}
		treposit = new(big.Int).Add(nodisamount, distre)
	}

	if treposit.Cmp(rtreposit) != 0 {
		return fmt.Errorf("treposit calculation fail ")
	}

	rfee, ok := new(big.Int).SetString(mer.Free, 10)
	if !ok {
		return fmt.Errorf("Fee parameter resolution to big.int failed ")
	}
	tamount, ok := new(big.Int).SetString(mer.Tamount, 10)
	if !ok {
		return fmt.Errorf("Tamount parameter resolution to big.int failed ")
	}
	fee, err := dividecalc(tamount, mer.Feerate)
	if err != nil {
		return err
	}
	if fee.Cmp(rfee) != 0 {
		return fmt.Errorf("fee calculation fail ")
	}
	profit := new(big.Int).Sub(tamount, new(big.Int).Add(rfee, rtreposit)) //计算分成
	if profit.Cmp(big.NewInt(0)) == -1 {
		return fmt.Errorf("profit is negative ")
	}

	//进行分成数据
	err = dividesTransaction(mer.Mid, treposit, stub)
	if err != nil {
		return err
	}
	return err
}

/*
	共享惠订单交易
*/
func tickerorders(args []string, stub shim.ChaincodeStubInterface) error {
	/*
		由于共享惠还没有进行确定的订单交易所以这部分先进行伪代码的记录
			1：计算实际支付是否正确，
			2：计算手续费总和是否正确
			3：计算利润
			4：是否需要进行商户金额的清分（待定）//待办
			5：合作方分成
			6：最终分成
	*/
	//commanName, err := getCertificateCommonName(stub)
	//if err != nil {
	//	return err
	//}
	ticketcanName := "coutcantm"
	ticketName := "coutickets"
	codeName := "couticketcodes"
	oTx := &OrderTx{}
	err := json.Unmarshal([]byte(args[0]), oTx)
	if err != nil {
		return fmt.Errorf("OrderTx struct json unmarshal fail,err : %s ", err)
	}
	tickMerKey := StoragePrefix + "_" + ticketcanName + "_" + oTx.Mid + "_" + oTx.Odesc.Tid //券可用门店key
	tickM, err := getState(tickMerKey, stub)
	if err != nil {
		return err
	}
	tickKey := StoragePrefix + "_" + ticketName + "_" + oTx.Odesc.Tid //券key
	tick, err := getState(tickKey, stub)
	if err != nil {
		return err
	}
	resid, _ := tickM["resid"].(float64)      //剩余可用码数
	discount, _ := tick["discount"].(float64) //得到券面值
	fieldkeys := []string{"status"}           // 码状态
	var checkvalue, upvalue []interface{}
	if resid == 0 {
		return fmt.Errorf("The current merchant has been unable to use the %s ticket ", oTx.Odesc.Tid)
	}
	oamount, ok := new(big.Int).SetString(oTx.Oamount, 10)
	if !ok {
		return fmt.Errorf("Oamount parameter resolution to big.int failed ")
	}
	pamount, ok := new(big.Int).SetString(oTx.Pamount, 10)
	if !ok {
		return fmt.Errorf("Pamount parameter resolution to big.int failed ")
	}
	outfree, ok := new(big.Int).SetString(oTx.Outfree, 10)
	if !ok {
		return fmt.Errorf("Outfree parameter resolution to big.int failed ")
	}
	infree, ok := new(big.Int).SetString(oTx.Infree, 10)
	if !ok {
		return fmt.Errorf("Infree parameter resolution to big.int failed ")
	}
	partnersfree, ok := new(big.Int).SetString(oTx.Partnersfree, 10)
	if !ok {
		return fmt.Errorf("Partnersfree parameter resolution to big.int failed ")
	}
	free, ok := new(big.Int).SetString(oTx.Free, 10)
	if !ok {
		return fmt.Errorf("Free parameter resolution to big.int failed ")
	}
	if oTx.Odesc.TicketType == 1 { //如果是折扣券则只能使用一张券
		if len(oTx.Odesc.Codes) > 1 {
			return fmt.Errorf("Only one coupon can be used ")
		}
		codeKey := StoragePrefix + "_" + codeName + "_" + oTx.Odesc.Codes[0]
		checkvalue[0] = float64(1)
		upvalue[0] = float64(2)
		_, err = checkandUpdate(codeKey, fieldkeys, checkvalue, upvalue, stub)
		if err != nil {
			return err
		}
		camount, _ := dividecalc(oamount, discount)
		if camount.Cmp(pamount) != 0 {
			return fmt.Errorf("pamount calculation fail ")
		}
	} else {
		for i, v := range oTx.Odesc.Codes {
			codeKey := StoragePrefix + "_" + codeName + "_" + v
			checkvalue[i] = float64(1)
			upvalue[i] = float64(2)
			_, err = checkandUpdate(codeKey, fieldkeys, checkvalue, upvalue, stub)
			if err != nil {
				return err
			}
		}
		length := int64(len(oTx.Odesc.Codes))
		disint := int64(discount) //将discount 转化为整数
		camount := new(big.Int).Sub(oamount, big.NewInt(disint*length))
		if camount.Cmp(pamount) != 0 {
			return fmt.Errorf("pamount calculation fail ")
		}
	}
	cfree := new(big.Int).Add(new(big.Int).Add(outfree, infree), partnersfree)
	if free.Cmp(cfree) != 0 {
		return fmt.Errorf("free calculation fail ")
	}
	income := new(big.Int).Sub(pamount, free)
	treposit, err := dividecalc(income, oTx.Norate)
	if err != nil {
		return err
	}
	receiver := &TransferAccount{
		AccountType: "coupartners",
		AccountId:   oTx.Pid,
		FundType:    "benefit",
	}
	err = transfer(partnersfree, nil, receiver, "", stub)
	if err != nil {
		return err
	}
	//netincome := new(big.Int).Sub(income,treposit)
	err = dividesTransaction(oTx.Mid, treposit, stub)
	if err != nil {
		return err
	}
	return nil
}

/*
	根据商户Id,进行分成交易
*/
func dividesTransaction(mid string, treposit *big.Int, stub shim.ChaincodeStubInterface) error {
	merkey := StoragePrefix + "_" + "merchants" + "_" + mid
	mermap, err := getState(merkey, stub)
	if err != nil {
		return err
	}
	aid, ok := mermap["aid"].(string) //得到代理商Id
	if !ok {
		return fmt.Errorf("aid parameter resolution to string failed ")
	}
	bid, ok := mermap["bid"].(string) //得到分支机构Id
	if !ok {
		return fmt.Errorf("Free parameter resolution to string failed ")
	}
	acid, ok := mermap["acid"].(string) //得到加盟商Id
	if !ok {
		return fmt.Errorf("Free parameter resolution to string failed ")
	}
	fid, ok := mermap["fid"].(string) //得到保理
	if !ok {
		return fmt.Errorf("Free parameter resolution to string failed ")
	}
	asdiscounts, err := divide(aid, treposit, stub)
	if err != nil {
		return err
	}
	bsdiscounts, err := divide(bid, treposit, stub)
	if err != nil {
		return err
	}
	acsdiscounts, err := divide(acid, treposit, stub)
	if err != nil {
		return err
	}
	fsdiscounts, err := divide(fid, treposit, stub)
	if err != nil {
		return err
	}
	if asdiscounts+bsdiscounts+acsdiscounts+fsdiscounts > 1 {
		return fmt.Errorf("The divide proportion must not exceed 1 ")
	}
	return nil
}

func divide(id string, treposit *big.Int, stub shim.ChaincodeStubInterface) (float64, error) {
	akey := StoragePrefix + "_" + "alibusi" + "_" + id
	amap, err := getState(akey, stub)
	if err != nil {
		return 0, err
	}
	sdiscounts, ok := amap["sdiscounts"].(float64)
	if !ok {
		return 0, fmt.Errorf("sdiscounts parameter resolution to float64 failed ")
	}
	adivide, err := dividecalc(treposit, sdiscounts)
	if err != nil {
		return 0, err
	}
	areceiver := &TransferAccount{
		AccountType: "alibusi",
		AccountId:   id,
		FundType:    "amount",
	}

	return sdiscounts, transfer(adivide, nil, areceiver, "", stub)
}

/*
	特别说明interface 不能是引用类型
*/
func checkandUpdate(key string, fieldkeys []string, checkvalue, upvalue []interface{}, stub shim.ChaincodeStubInterface) (map[string]interface{}, error) {
	valueMap, err := getState(key, stub)
	if err != nil {
		return nil, err
	}
	for i, fk := range fieldkeys {
		if !reflect.DeepEqual(valueMap[fk], checkvalue[i]) {
			return nil, fmt.Errorf("%s not the expected field value", fk)
		} else {
			valueMap[fk] = upvalue[i]
		}
	}
	value, err := json.Marshal(valueMap)
	if err != nil {
		return nil, err
	}
	err = stub.PutState(key, value)
	if err != nil {
		return nil, err
	}

	return valueMap, nil
}

/*
	实现普通的转账操作
*/
func transfer(amount *big.Int, sender, receiver *TransferAccount, commonName string, stub shim.ChaincodeStubInterface) error {
	var err error
	if amount.Cmp(big.NewInt(0)) == -1 {
		err = fmt.Errorf("transfer limit must greater than 0 ")
		return err
	}
	if sender != nil {
		senderSchema := &Schema{}
		senderSchema ,err= getSchema(sender.AccountType, stub)
		if err != nil {
			return err
		}
		senderKey := LeadgerPrefix + "_" + sender.AccountType + "_" + sender.AccountId
		senderleadgermap, err := getState(senderKey, stub)
		if err != nil {
			return err
		}
		onwer, ok := senderleadgermap["common_name"].(string)
		err = senderSchema.AuthorityCheck(senderSchema.LeadgerFields[sender.FundType].PAuth.Transfer, commonName, onwer)
		if err !=nil {
			err = fmt.Errorf("%s field issue permission check does not match, err: %s ", sender.FundType, err)
			return err
		}
		fundstr := senderleadgermap[sender.FundType].(string)
		fund, ok := new(big.Int).SetString(fundstr, 10)
		if !ok {
			err = fmt.Errorf("%s parameter resolution to big.int failed ", sender.FundType)
			return err
		}
		if fund.Cmp(amount) == -1 {
			err = fmt.Errorf("The current amount is less than the withdrawal amount and cannot be extracted ")
			return err
		}
		fund.Sub(fund, amount)
		senderleadgermap[sender.FundType] = fund.String()

		err = putState(senderKey, senderleadgermap, stub)
		if err != nil {
			return err
		}
	}
	if receiver != nil {
		receiverKey := LeadgerPrefix + "_" + receiver.AccountType + "_" + receiver.AccountId
		receiverleadgermap, err := getState(receiverKey, stub)
		if err != nil {
			return err
		}
		rfundstr := receiverleadgermap[receiver.FundType].(string)
		rfund, ok := new(big.Int).SetString(rfundstr, 10)
		if !ok {
			err = fmt.Errorf("%s parameter resolution to big.int failed ", receiver.FundType)
			return err
		}
		rfund.Add(rfund, amount)
		receiverleadgermap[receiver.FundType] = rfund.String()

		err = putState(receiverKey, receiverleadgermap, stub)
		if err != nil {
			return err
		}
	}
	return nil
}
