package main

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"time"
)

type GXHCC struct {
}

type AuthGroup struct {
	Users map[string]int64 `json:"users"` //用户组权限，默认使用用户组。我们使用形如：“User1”:timestamp. key 表示用户或者角色，value 表示有效时间。我们使用格林威治时间时间戳
	Roles map[string]int64 `json:"roles"` //角色组权限，
}

type Auth struct {
	Read  AuthGroup `json:"read"`  // 字段的读权限,“”,不存在权限，
	Write AuthGroup `json:"write"` //字段的写权限
}

type SchemaParameters struct {
	Value interface{} `json:"val"` //默认值  这里特别说明一下如果这里是backUp 字段，我们不进行更深一步的校验，包括格式
	PAuth Auth        `json:"auth"`
}

type Schema struct {
	Fields     map[string]SchemaParameters `json:"fields"`
	SchemaAuth Auth                        `json:"schema_auth"` //schema 的写权限，主要是用在 schema update 部分
	SAuth      Auth                        `json:"auth"`
}

var (
	defValue = "***" //查询用户权限不足的情况下默认返回值
	PREFIX   = "GXH"
	IsUsers  = false //是否启用用户权限，false 表示启用Roles
)

// Init does nothing for this cc
func (t *GXHCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success([]byte("GXHCC init successful! "))
}

// Invoke for this chaincode exposes
func (t *GXHCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	// get arguments and transient
	functionName, args := stub.GetFunctionAndParameters()

	switch functionName {
	case "authority":

	case "schema":
		res, err := SchemaProcess(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("SchemaProcess function err: %s", err))
		}
		return shim.Success([]byte(res))

	case "put":
		res, err := put(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("put function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "get":
		res, err := get(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("get function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "update":
		res, err := update(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("update function err: %s", err))
		}
		return shim.Success([]byte(res))

	default:
		return shim.Error(fmt.Sprintf("Unsupported function %s", functionName))
	}
	return shim.Success([]byte("GXHCC invoke successful "))
}

/*
	put
	args 字段说明：
	1：args[0]代表我们具体操作的表，
    2：args[1:]后面的数据表示要记录到数据库的数据,每一个string 必须是json字符串，并且需要每一个json 字符串都必须包含id 字段

*/
func put(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) < 2 {
		return "", fmt.Errorf("Expected  more than 2 parameters to function put!")
	}
	var PutDatas []map[string]interface{}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	schema := &Schema{}
	result, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(result), &schema); err != nil {
		return "", fmt.Errorf("json Unmarshal schemaRes err: %s ", err)
	}
	if ok, err := authorityCheck(schema.SAuth.Write, commonName); !ok {
		return "", fmt.Errorf("write %s table fail,err: %s ", args[0], err)
	}
	if err := json.Unmarshal([]byte(args[1]), &PutDatas); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json array, err:%s", err)
	}
	for k, mapres := range PutDatas {
		Id, ok := mapres["id"]
		if !ok {
			return "", fmt.Errorf("The id field must exist ")
		}
		//if result, err := stub.GetState(args[0] + Id.(string)); err != nil {
		//	return "", fmt.Errorf("get %s data happen err: ", args[0]+Id.(string))
		//}
		//else if result != nil {
		//	return "", fmt.Errorf("%s data already exist", args[0]+Id.(string))
		//}
		if !SchemaCheck(schema.Fields, mapres) {
			return "", fmt.Errorf("SchemaCheck fail,args index %d string doesn't match %s format", k, args[0])
		}
		putDate, _ := json.Marshal(mapres)
		if err := stub.PutState(PREFIX+"_"+args[0]+"_"+Id.(string), putDate); err != nil {
			return "", fmt.Errorf("PutState fail, err :%s", err)
		}
	}
	return fmt.Sprintf("%s put data success!", args[0]), nil
}

/*
	修改一个表的一条数据的一个或者多个字段值
*/
func update(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("Expected 2 parameters to function update! ")
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	schema := &Schema{}
	schemaRes, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(schemaRes), &schema); err != nil {
		return "", fmt.Errorf("json Unmarshal schemaRes err: %s ", err)
	}
	var Updata map[string]interface{}
	var allMap map[string]interface{}
	if err := json.Unmarshal([]byte(args[1]), &Updata); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args  string not json string,err: %s", err)
	}
	Id, ok := Updata["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	if ok, _ := authorityCheck(schema.SAuth.Write, commonName); !ok {
		if ok, err := parsauthorityCheck(schema.Fields, Updata, commonName); !ok {
			return "", fmt.Errorf("parsauthorityCheck err: %s", err)
		}
	}
	result, err := stub.GetState(PREFIX + "_" + args[0] + "_" + Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("GetState data fail,please check your parameters ")
	}
	if err := json.Unmarshal(result, &allMap); err != nil {
		return "", fmt.Errorf("result parameters Unmarshal fail err:%s ", err)
	}
	if err := Deassign(Updata, allMap); err != nil {
		return "", err
	}

	resultByte, _ := json.Marshal(allMap)
	if err := stub.PutState(PREFIX+"_"+args[0]+"_"+Id.(string), resultByte); err != nil {
		return "", fmt.Errorf("updateState date fail, err :%s ", err)
	}
	return fmt.Sprintf("%s update data success!", args[0]), nil

}

func get(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("Expected  2 parameters to function get!")
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	var parameters map[string]interface{}
	var mapResult map[string]interface{}

	if err := json.Unmarshal([]byte(args[1]), &parameters); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	}

	Id, ok := parameters["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(PREFIX + "_" + args[0] + "_" + Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("GetState data fail,please check your parameters ")
	}
	schema := &Schema{}
	schemaRes, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(schemaRes), &schema); err != nil {
		return "", fmt.Errorf("json Unmarshal schemaRes err: %s ", err)
	}
	if err := json.Unmarshal(result, &mapResult); err != nil {
		return "", fmt.Errorf(" Unmarshal fail,result err: %s", err)
	}
	if ok, _ := authorityCheck(schema.SAuth.Read, commonName); !ok {
		if filteredData, err := dataFilter(schema.Fields, mapResult, commonName); err != nil {
			return "", fmt.Errorf(" dataFilter fail, err: %s", err)
		} else {
			result, _ := json.Marshal(filteredData)
			return string(result), nil
		}

	} else {
		return string(result), nil
	}
}

/*
	重新赋值，并检查相应的格式是否正确
*/
func Deassign(upMap, result map[string]interface{}) error {
	if len(upMap) > len(result) {
		return fmt.Errorf("Deassign fail,reassigned data length greater than the original data length! ")
	}
	for k, v := range upMap {
		if resultV, ok := result[k]; ok {
			if resultMap, resultok := resultV.(map[string]interface{}); resultok {
				if vMap, ok := v.(map[string]interface{}); !ok {
					return fmt.Errorf("Deassign fail, format doesn't match! ")
				} else {
					Deassign(vMap, resultMap)
				}
			} else {
				result[k] = v
			}
		} else {
			return fmt.Errorf("Deassign fail %s key nonentity ", k)
		}
	}
	return nil
}

func SchemaCheck(schema map[string]SchemaParameters, checkmap map[string]interface{}) bool {
	if len(schema) != len(checkmap) {
		return false
	}
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return false
		} else {
			if parameterS, ok := schemaV.Value.(map[string]interface{}); ok {
				if parameterC, ok := v.(map[string]interface{}); !ok {
					return false
				} else {
					if !BackUpCheck(parameterS, parameterC) {
						return false
					}
				}

				//else {
				//	var backUp map[string]SchemaParameters
				//	backUpByte,_ := json.Marshal(parameterS)
				//	if err := json.Unmarshal(backUpByte,&backUp);err != nil{
				//		return false
				//	}
				//	if !SchemaCheck(backUp, parameterC) {
				//		return false
				//	}
				//}
			}
		}

	}
	return true
}

func BackUpCheck(backUp map[string]interface{}, checkmap map[string]interface{}) bool {
	if len(backUp) != len(checkmap) {
		return false
	}
	for k := range checkmap {
		if _, ok := backUp[k]; !ok {
			return false
		}

	}
	return true
}

func SchemaProcess(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) < 2 {
		return "", fmt.Errorf("Expect features with 2 or more parameters to play! ")
	}
	schema := &Schema{}
	switch args[0] {

	case "put":
		return schema.put(args[1:], stub)
	case "update":
		return schema.update(args[1:], stub)
	case "get":
		return schema.getSchema(args[1], stub)
	default:
		return "", fmt.Errorf(fmt.Sprintf("Unsupported schema function of %s", args[0]))

	}
}

/*
	put
	args 字段说明：
	1：args[0]代表我们具体操作的表，
    2：args[1:]后面的数据表示要记录到数据库的数据,每一个string 必须是json字符串，并且需要每一个json 字符串都必须包含id 字段

我们可以发现json字符串都被我们解析成了map[string]interface{}类型的map,其中key代表表模板的字段，interface可以是一个结构，我们当前将他看做我们表模板字段的初始值。

*/
func (this *Schema) put(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("Expected  2 parameters to function put!")
	}
	var schema *Schema
	var err error
	if schema, err = NewSchema(args[1]); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	} else {
		if backupValue, ok := schema.Fields["backup"]; ok {
			if _, ok := backupValue.Value.(map[string]interface{}); !ok {
				return "", fmt.Errorf("parameters Unmarshal fail,extra string not json string,err: %s", err)
			}
			//var backUp map[string]SchemaParameters
			//backUpByte, _ := json.Marshal(backupValue.Value)
			//if err := json.Unmarshal(backUpByte, &backUp); err != nil {
			//	return "", fmt.Errorf("backup value parameters struct err %s ", err)
			//}
		}
	}

	//if err := json.Unmarshal([]byte(args[1]), &schema); err != nil {
	//	return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	//} else {
	//
	//}
	_, ok := schema.Fields["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	if value, _ := stub.GetState(PREFIX + "_" + args[0]); value != nil {
		return "", fmt.Errorf("the parameter %s already exists and cannot be added", args[0])
	}
	putDate, _ := json.Marshal(schema) //处理重复字段
	if err := stub.PutState(PREFIX+"_"+args[0], putDate); err != nil {
		return "", fmt.Errorf("PutState fail,err: %s", err)
	}
	return fmt.Sprintf("%s schema put success!", args[0]), nil
}

/*
	if update Schema 那么我们没有办法比较旧有的一个模板，
     Schema 的 update,不支持部分修改，因为存在增加和删除某些字段的情况。
	TODO,增加版本管理
*/
func (this *Schema) update(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("expected 2 parameters to function put! ")
	}
	var schema *Schema
	var err error
	if schema, err = NewSchema(args[1]); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	} else {
		if backupValue, ok := schema.Fields["backup"]; ok {
			if _, ok := backupValue.Value.(map[string]interface{}); !ok {
				return "", fmt.Errorf("parameters Unmarshal fail,extra string not json string,err: %s", err)
			}
		}
	}
	_, ok := schema.Fields["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(PREFIX + "_" + args[0])
	if err != nil || result == nil {
		return "", fmt.Errorf("%s schema data doesn't exist,please check your parameters ", args[0])
	}

	if err := json.Unmarshal([]byte(result), &schema); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail, err: %s", err)
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	if ok, err := authorityCheck(schema.SchemaAuth.Write, commonName); !ok {
		return "", fmt.Errorf("update %s schema fail,err: %s ", args[0], err)
	}
	if err = stub.PutState(PREFIX+"_"+args[0], []byte(args[1])); err != nil {
		return "", fmt.Errorf("%s schema data PutState fail,please try again ", args[0])
	}
	return fmt.Sprintf("%s schema update success!", args[0]), err

}

func (this *Schema) get(args string, stub shim.ChaincodeStubInterface) (string, error) {
	result, err := stub.GetState(PREFIX + "_" + args)
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema data fail,please check your parameters ")
	}
	return string(result), nil
}

func (this *Schema) getSchema(args string, stub shim.ChaincodeStubInterface) (string, error) {
	result, err := stub.GetState(PREFIX + "_" + args)
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema data fail,please check your parameters ")
	}
	if err := json.Unmarshal(result, this); err != nil {
		return "", fmt.Errorf("SchemaData Unmarshal fail, err: %s", err)
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	if ok, err := authorityCheck(this.SchemaAuth.Read, commonName); !ok {
		return "", fmt.Errorf("get %s schema fail,err: %s", args, err)
	}
	return string(result), nil
}

func getCertificateCommonName(stub shim.ChaincodeStubInterface) (string, error) {
	return "admin", nil
	creatorByte, _ := stub.GetCreator()
	certStart := bytes.IndexAny(creatorByte, "-----BEGIN")
	if certStart == -1 {
		return "", fmt.Errorf("No certificate found ")
	}
	certText := creatorByte[certStart:]
	bl, _ := pem.Decode(certText)
	if bl == nil {
		return "", fmt.Errorf("Could not decode the PEM structure ")
	}

	cert, err := x509.ParseCertificate(bl.Bytes)
	if err != nil {
		return "", fmt.Errorf("ParseCertificate failed")
	}
	return cert.Subject.CommonName, nil
}

/*
	检查一组参数的权限是否匹配
*/
func parsauthorityCheck(schema map[string]SchemaParameters, checkmap map[string]interface{}, commonName string) (bool, error) {
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return false, fmt.Errorf("Update data parameter mismatch with schema structure! ")
		} else {
			if _, ok := schemaV.Value.(map[string]interface{}); ok {
				if _, ok := v.(map[string]interface{}); !ok {
					return false, fmt.Errorf("Update data parameter mismatch with schema structure! ")
				}
			}
			//	if !authorityCheck(schemaV.Write, commonName) {
			//		return false, fmt.Errorf("%s field permission check does not match", k)
			//	}
			//	else {
			//		if res, err := parsauthorityCheck(parameterS, parameterC, commonName); !res {
			//			return res, err
			//		}
			//	}
			//} else {
			if ok, err := authorityCheck(schemaV.PAuth.Write, commonName); !ok {
				return false, fmt.Errorf("%s field permission check does not match, err: %s ", k, err)
			}
			//}
		}

	}
	return true, nil
}

/*
	根据权限拼装数据,进行数据过滤
*/
func dataFilter(schema map[string]SchemaParameters, checkmap map[string]interface{}, commonName string) (map[string]interface{}, error) {
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return nil, fmt.Errorf("Update data parameter mismatch with schema structure! ")
		} else {
			if _, ok := schemaV.Value.(map[string]SchemaParameters); ok {
				if _, ok := v.(map[string]interface{}); !ok {
					return nil, fmt.Errorf("Update data parameter mismatch with schema structure! ")
				}
				if ok, _ := authorityCheck(schemaV.PAuth.Read, commonName); !ok {
					checkmap[k] = defValue
				}
				//else {
				//	if _, err := dataFilter(parameterS, parameterC, commonName); err!=nil {
				//		return nil, err
				//	}
				//}
			} else {
				if ok, _ := authorityCheck(schemaV.PAuth.Read, commonName); !ok {
					checkmap[k] = defValue
				}
			}
		}
	}
	return checkmap, nil
}

func authorityCheck(authority AuthGroup, commonName string) (bool, error) {
	var auths map[string]int64
	if IsUsers {
		auths = authority.Users
	} else {
		auths = authority.Roles
	}
	if len(auths) == 0 {
		return true, nil
	}
	for k, v := range auths {
		if k == commonName {
			if v == 0 || v > time.Now().Unix() {
				return true, nil
			} else {
				return false, fmt.Errorf("%s user permission period ", commonName)
			}
		}
	}
	return false, fmt.Errorf("%s user does not have permission", commonName)

}

func NewSchema(arg string) (*Schema, error) {
	schema := &Schema{
		Fields: make(map[string]SchemaParameters),
	}
	var argMap map[string]interface{}
	if err := json.Unmarshal([]byte(arg), &argMap); err != nil {
		return nil, err
	}
	if argMap["fields"] != nil {
		if err := json.Unmarshal([]byte(arg), schema); err != nil {
			return nil, err
		}
		return schema, nil
	}
	for k, v := range argMap {
		schema.Fields[k] = SchemaParameters{
			Value: v,
			PAuth: Auth{},
		}
	}
	return schema, nil
}

func main() {
	err := shim.Start(&GXHCC{})
	if err != nil {
		fmt.Printf("Error starting EncCC chaincode: %s", err)
	}
}
