package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"strconv"
)

const (
	CompanyInfoKey  = "company_key"
	BizInfoDtoKey = "biz_info_dto_key"
	//ChipUsedDtoKey = "chip_used_dto_key"
	UsedKey = "used_key"
	InvoiceInfoDtoKey = "invoice_info_dto"
)

type Medical struct {
}

/*
  公司信息
*/
type CompanyInfo struct {
	Id uint64`json:"id"`
	BusinessLicense  string `json:"businessLicense"`
	Cname 		string `json:"cname"`
	CmpanyAddress  string `json:"cmpanyAddress"`
	CompanyDesc  string `json:"companyDesc"`
	CompanyType  string `json:"companyType"`
	ContactPhone string `json:"contactPhone"`
	Contacts    string `json:"contacts"`
	LegalPerson  string `json:"legalPerson"`
	Status   int `json:"status"`
}

//业务信息上链
type BizInfoDto struct {
	Id  	uint64 `json:"id"`  //业务信息ID.
	Cid     uint64 `json:"cid"`  //公司ID
	Cname   string `json:"cname"`  //公司名称.
	UserId  uint64 `json:"userId"`  //医代Id
	UserName string `json:"userName"`  //医代名称
	IdNo    string  `json:"idNo"`   //身份证号
	Mobile  string `json:"mobile"`  //手机号
	Lno   string   `json:"lno"`   //物流单号
	TradeDate string `json:"tradeDate"`  //交易日期
	TradeAddress string `json:"tradeAddress"`  //交易地址
	Hospital   string `json:"hospital"`  //供给医院
	Amount   string `json:"amount"`   //交易金额
	Remark   string `json:"remark"`    //备注
	CreateTime string `json:"createTime"` //创建时间
	UpdateTime string `json:"updateTime"`  //更新时间
	ImageKeys  []string  `json:"imageKeys"`  //上传图片Key.
	FileKeys   []string `json:"fileKeys"`   //上传文件keys
	Chips []ChipPacking `json:"chips"`  //芯片包装信息.
}

//芯片包装信息.
type ChipPacking struct {
	ChipSno    string  `json:"chipSno"`//芯片序列号
	PackInfo   string  `json:"packInfo"`// 包装信息
	PackCount  uint64  `json:"packCount"`//包装数量
}

//前端芯片使用
type ChipUsedDto struct {
	Lno   string   `json:"lno"`   //物流单号
	ChipSno  string `json:"chipSno"` //芯片序列号
	ProductNo string `json:"productNo"` //服务产品码
	UserId   uint64  `json:"userId"`  //使用者ID
	UsedTime string  `json:"usedTime"`  //使用时间
	ImageKeys  []string  `json:"imageKeys"`  //上传图片Key.
	FileKeys   []string `json:"fileKeys"`   //上传文件keys
	VideoKeys []string `json:"videoKeys"`  //上传视频Key.
}

type FirstInvoiceInfoDto struct {
	Id  	uint64 `json:"id"`  //发票ID.
	Cid     uint64 `json:"cid"`  //公司ID
	Cname   string `json:"cname"`  //公司名称.
	InvoiceNo string `json:"invoiceNo"` //发票单号
	BillingUnit string `json:"billingUnit"`  //开票单位
	ReceiveUnit string `json:"receiveUnit"`  //接票单位
	Amount   float64 `json:"amount"`   //交易金额
	BillingType int `json:"billingType"`  //开票类型
	Remark   string `json:"remark"`    //备注
	InvoiceType int `json:"invoiceType"`  //  发票类型 1公司发票 2技术人员发票
	LnoKeys   []string `json:"lnoKeys"`  //物流单号key
	ImageKeys  []string  `json:"imageKeys"`  //上传图片Key.
	FileKeys   []string `json:"fileKeys"`   //上传文件keys
}

type InvoiceInfoDto struct {
	Id  	uint64 `json:"id"`  //发票ID.
	Cid     uint64 `json:"cid"`  //公司ID
	Cname   string `json:"cname"`  //公司名称.
	Uid     uint64 `json:"uid"`  //技术人员ID
	UserName string `json:"userName"`  //医代名称
	IdNo    string  `json:"idNo"`   //身份证号
	Mobile  string `json:"mobile"`  //手机号
	//TaxId   string   `json:"taxId"`   //税号
	InvoiceNo string `json:"invoiceNo"` //发票单号
	Lno   string   `json:"lno"`   //物流单号
	BillingUnit string `json:"billingUnit"`  //开票单位
	BillingType int `json:"billingType"`  //开票类型
	ReceiveUnit string `json:"receiveUnit"`  //接票单位
	Amount   float64 `json:"amount"`   //交易金额
	Remark   string `json:"remark"`    //备注
	InvoiceType int `json:"invoiceType"`  //  发票类型 1公司发票 2技术人员发票
	ProductCodes []string `json:"ProductCodes"` //服务产品码
	LnoKeys   []string `json:"lnoKeys"`  //物流单号key
	ImageKeys  []string  `json:"imageKeys"`  //上传图片Key.
	FileKeys   []string `json:"fileKeys"`   //上传文件keys
}

type TransferList struct{
	InvoiceInfo *InvoiceInfoDto `json:"invoice_info"`
	BussinessInfo *BizInfoDto	`json:"bussiness_info"`
	ChipUsedDtoS map[string]*ChipUsedDto `json:"chip_used_dto_s"`
}

func (med *Medical)Init(stub shim.ChaincodeStubInterface) pb.Response{
	return  shim.Success([]byte("medical chain init success"))
}

// Invoke is called to update or query the ledger in a proposal transaction.
// Updated state variables are not committed to the ledger until the
// transaction is committed.
func (med *Medical)Invoke(stub shim.ChaincodeStubInterface) pb.Response{
	functionName, args := stub.GetFunctionAndParameters()
	switch functionName {
	case "addCompanyInfo": //增加公司信息
		return AddCompanyInfo( args, stub)
	case "updateCompanyInfo": //修改公司信息
		return AddCompanyInfo( args, stub)
	case "addInvoiceInfo": //增加发票信息
		return AddInvoiceInfo( args, stub)
	case "updateInvoiceInfo": //修改发票信息
		return AddInvoiceInfo( args, stub)
	case "addBusinessInfo": //增加业务信息
		return AddBusinessInfo( args, stub)
	case "updateBusinessInfo": //修改业务信息
		return AddBusinessInfo( args, stub)
	case "addUseInfo":  //技术人员增加使用信息
		return AddUseInfo(args, stub)
	case "searchTransferList":
		return SearchTransferListByInvoiceNo(args,stub)
	case "testCheck": //测试接口
		return TestCheck(args, stub)
	default:
		return shim.Error(fmt.Sprintf("%s Unsupported function name ", functionName))
	}
	return pb.Response{}
}

func TestCheck(args []string, stub shim.ChaincodeStubInterface) pb.Response {
	if len(args) < 1 {
		return shim.Success([]byte("参数内容为空"))
	}
	return shim.Success([]byte(args[0]))
}
/*
	增加/编辑 公司信息
*/
func AddCompanyInfo(args []string,stub shim.ChaincodeStubInterface) pb.Response{
	if len(args) > 1 {
		return shim.Error("add company info expected 1 parameters! ")
	}
	comInfo := &CompanyInfo{}
	err := json.Unmarshal([]byte(args[0]), comInfo)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal CompanyInfo fail,err %s ", err))
	}
	id:= strconv.FormatUint(comInfo.Id,10)
	if comInfo.Id == uint64(0){
		return shim.Error("company info id can not is zero")
	}
	companyKey := CompanyInfoKey+"_"+id
	err = putStateStruct(companyKey,comInfo,stub)
	if err !=nil{
		return shim.Error("put company info err: "+ err.Error())
	}
	return shim.Success([]byte("put "+comInfo.Cname+" company info successful!"))
}

/*
	增加/编辑 发票信息
*/
func AddInvoiceInfo(args []string,stub shim.ChaincodeStubInterface) pb.Response{
	if len(args) > 1 {
		return shim.Error("add invoice info expected 1 parameters! ")
	}
	invoiceInfo := &InvoiceInfoDto{}
	err := json.Unmarshal([]byte(args[0]), invoiceInfo)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal invoice info fail,err %s ", err))
	}
	if invoiceInfo.InvoiceNo ==" " ||len(invoiceInfo.InvoiceNo) == 0 {
		return shim.Error("invoice info InvoiceNo can not is null")
	}
	companyKey := InvoiceInfoDtoKey+"_"+invoiceInfo.InvoiceNo
	err = putStateStruct(companyKey,invoiceInfo,stub)
	if err !=nil{
		return shim.Error("put invoice info err: "+ err.Error())
	}
	return shim.Success([]byte("put "+invoiceInfo.InvoiceNo+" invoice info successful!"))
}

/*
	增加业务信息
*/
func AddBusinessInfo(args []string,stub shim.ChaincodeStubInterface) pb.Response{

	if len(args) > 1 {
		return shim.Error("add business info expected 1 parameters! ")
	}
	bizInfo := &BizInfoDto{}
	err := json.Unmarshal([]byte(args[0]), bizInfo)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal BizInfoDto fail,err %s ", err))
	}
	if bizInfo.Lno ==" " ||len(bizInfo.Lno) == 0 {
		return shim.Error("business info Lno can not is null")
	}
	bizInfoKey := BizInfoDtoKey+"_"+bizInfo.Lno
	err = putStateStruct(bizInfoKey,bizInfo,stub)
	if err !=nil{
		return shim.Error("put business info err: "+ err.Error())
	}
	return shim.Success([]byte("put "+bizInfo.Lno+" business info successful!"))
}

/*
   技术人员添加使用信息
*/
func AddUseInfo(args []string,stub shim.ChaincodeStubInterface) pb.Response{
	if len(args) > 1 {
		return shim.Error("add used info expected 1 parameters! ")
	}
	usedInfo := &ChipUsedDto{}
	err := json.Unmarshal([]byte(args[0]), usedInfo)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal used info fail,err %s ", err))
	}
	if usedInfo.Lno ==" " ||len(usedInfo.Lno) == 0 {
		return shim.Error("business info Lno can not is null")
	}
	usedCompositeKey,err := stub.CreateCompositeKey(UsedKey,[]string{usedInfo.Lno,usedInfo.ChipSno})
	if err !=nil{
		return shim.Error("create used info composite key err: "+ err.Error())
	}
	err = putStateStruct(usedCompositeKey,usedInfo,stub)
	if err !=nil{
		return shim.Error("put used info err: "+ err.Error())
	}
	return shim.Success([]byte("put "+usedInfo.ChipSno+" used info successful!"))
}

func SearchInvoiceInfoByNo(args []string,stub shim.ChaincodeStubInterface) ([]byte,error){
	if len(args) > 1 {
		return nil,errors.New("search invoice info expected 1 parameters! ")
	}
	if args[0] ==" " ||len(args[0]) == 0 {
		return nil,errors.New("search key can not is null")
	}
	invoiceInfoKey := InvoiceInfoDtoKey+"_"+args[0]
	return  getStateByte(invoiceInfoKey,stub)
}

func SearchBusinessInfoByLno(args []string,stub shim.ChaincodeStubInterface) ([]byte,error){
	if len(args) > 1 {
		return nil,errors.New("search business info expected 1 parameters! ")
	}
	if args[0] ==" " ||len(args[0]) == 0 {
		return nil,errors.New("search key can not is null")
	}
	businessInfoKey := BizInfoDtoKey+"_"+args[0]
	return getStateByte(businessInfoKey,stub)
}

func SearchUsedInfo(args []string,stub shim.ChaincodeStubInterface) ([]byte,error){
	if len(args) < 2 {
		return nil,errors.New("search used info expected 2 parameters! ")
	}
	Lno := args[0]
	ChipSno := args[1]
	if Lno ==" " ||len(Lno) == 0 {
		return nil,errors.New("search key lno can not is null")
	}
	if ChipSno ==" " ||len(ChipSno) == 0 {
		return nil,errors.New("search key chipSno can not is null")
	}
	usedCompositeKey,err := stub.CreateCompositeKey(UsedKey,[]string{Lno,ChipSno})
	if err !=nil{
		return nil,errors.New("create used info composite key err: "+ err.Error())
	}
	return  getStateByte(usedCompositeKey,stub)
}

func SearchUseds(args []string,stub shim.ChaincodeStubInterface) (useds [][]byte,err error){
	if len(args) < 1 {
		return nil,errors.New("search used info expected 1 parameters! ")
	}
	Lno := args[0]
	if Lno ==" " ||len(Lno) == 0 {
		return nil,errors.New("search key lno can not is null")
	}
	iterator,err:= stub.GetStateByPartialCompositeKey(UsedKey,[]string{Lno})
	for iterator.HasNext(){
		resultKV,err := iterator.Next()
		if err!=nil{
			return nil,errors.New("iterator query next used info err: "+ err.Error())
		}
		useds = append(useds,resultKV.Value)

	}
	return  useds,nil
}
/*
	根据发票单号查询流转轨迹
*/
func SearchTransferListByInvoiceNo(args []string,stub shim.ChaincodeStubInterface) pb.Response{
	var list TransferList
	invoiceInfoByte,err := SearchInvoiceInfoByNo(args,stub)
	if err != nil {
		return shim.Error(err.Error())
	}
	invoiceInfo := &InvoiceInfoDto{}
	err = json.Unmarshal(invoiceInfoByte, invoiceInfo)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal invoice info fail,err %s ", err))
	}
	list.InvoiceInfo = invoiceInfo
	businessByte,err := SearchBusinessInfoByLno([]string{invoiceInfo.Lno},stub)
	if err != nil {
		return shim.Error(err.Error())
	}
	business := &BizInfoDto{}
	err = json.Unmarshal(businessByte, business)
	if err != nil {
		return shim.Error(fmt.Sprintf("Unmarshal business info fail,err %s ", err))
	}
	list.BussinessInfo = business
	usedsByte,err := SearchUseds([]string{invoiceInfo.Lno},stub)
	if err != nil {
		return shim.Error(err.Error())
	}
	list.ChipUsedDtoS = make(map[string]*ChipUsedDto,len(usedsByte))
	for _,value :=range usedsByte{
		used := &ChipUsedDto{}
		err = json.Unmarshal(value, used)
		if err != nil {
			return shim.Error(fmt.Sprintf("Unmarshal used info fail,err %s ", err))
		}
		list.ChipUsedDtoS[used.ChipSno] = used
	}
	listByte,err := json.Marshal(list)
	if err!=nil {
		shim.Error("marshal transferList err "+err.Error())
	}
	return shim.Success(listByte)
}


func putStateStruct(key string, value interface{}, stub shim.ChaincodeStubInterface) error {
	valueByte, err := json.Marshal(value)
	if err != nil {
		return fmt.Errorf(" %s json marshal data fail,err: %s", key, err)
	}
	err = stub.PutState(key, valueByte)
	if err != nil {
		return fmt.Errorf("putState %s data fail,err: %s", key, err)
	}
	return nil
}

func getStateByte(key string, stub shim.ChaincodeStubInterface) ([]byte, error) {
	byteInfo, err := stub.GetState(key)
	if err != nil {
		return nil, err
	}
	if byteInfo == nil {
		return nil, fmt.Errorf("The query information is empty ")
	}
	return byteInfo, nil
}


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