package asyncLog

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	"hashrateNode/cache"
	hashrateCommon "hashrateNode/common"
	vm "hashrateNode/contract/VmContract"
	vmCreate "hashrateNode/contract/VmCreateContract"
	"hashrateNode/log"
	"hashrateNode/models"
	"hashrateNode/pveApi"
	"hashrateNode/utils"
	"math/big"
	"strconv"
	"strings"
	"time"
)

var tranOpts *bind.TransactOpts
var err error
var hostName, fromAddr string
var nonce uint64

func init() {
	tranOpts, err = utils.GetTranOpts()
	if err != nil {
		log.Error("GetTranOpts error:", err)
		return
	}
	hostName = beego.AppConfig.String("pve::host_name")
	fromAddr = beego.AppConfig.String("sendTranAddress")
}

func VmCreateContractHandler(vLog types.Log) error {
	logs.Info("handle vm create contract logs.")
	stakeAbiInfo, _ := abi.JSON(strings.NewReader(vmCreate.VmCreateABI))
	{
		method := vLog.Topics[0]
		switch method.String() {
		case hashrateCommon.VmCreateEvent:
			{
				rp, err := stakeAbiInfo.Unpack("AddCreateVMInfoEvent", vLog.Data)
				sockets, _ := new(big.Int).SetString(rp[4].(string), 10)
				cores, _ := new(big.Int).SetString(rp[5].(string), 10)
				gpuNum, _ := new(big.Int).SetString(rp[7].(string), 10)
				memory, _ := new(big.Int).SetString(rp[8].(string), 10)
				osType, _ := new(big.Int).SetString(rp[9].(string), 10)
				vmCreateEvent := &models.VmConfig{
					TaskId:   rp[0].(string),
					Owner:    rp[1].(common.Address),
					CallerIp: rp[2].(string),
					Cpu:      rp[3].(string),
					Sockets:  sockets,
					Cores:    cores,
					Gpu:      rp[6].(string),
					GpuNum:   gpuNum,
					Memory:   memory,
					OsType:   osType,
					Disk:     bighundred,
				}
				//vmCreateEvent := &models.VmConfig{}
				//err = stakeAbiInfo.UnpackIntoInterface(vmCreateEvent, "AddCreateVMInfoEvent", vLog.Data)
				if err != nil {
					cache.Redis.LpushByte(hashrateCommon.EventList, utils.Marshal(vmCreateEvent))
				}
				if !IsCreateVmCondition(vmCreateEvent.CallerIp) {
					return nil
				}
				isSnatch, err := snatchTask(vmCreateEvent)
				if err != nil || !isSnatch {
					return err
				}
			}
		}
	}
	return nil
}

// IsCreateVmCondition 本机是否符合创建Vm的要求
func IsCreateVmCondition(callerIp string) bool {
	localIp := beego.AppConfig.String("local_ip")
	distance := utils.CalculateDistance(utils.GetIpAddr(callerIp), utils.GetIpAddr(localIp))
	if distance <= 1000 {
		return true
	}
	return true
}

// snatchTask 进行抢占任务
func snatchTask(vmCfg *models.VmConfig) (bool, error) {
	rpcUrl := beego.AppConfig.String("chain_rpc_url")
	client, err := ethclient.Dial(rpcUrl)
	if err != nil {
		log.Error("Eth client.Dial error:", err)
		return false, err
	}
	defer client.Close()
	nonce, err = utils.EthGetTransactionCount(client, fromAddr)
	if err != nil {
		return false, err
	}
	vmCreate, err := vmCreate.NewVmCreate(common.HexToAddress(hashrateCommon.VmCreateContract), client)
	if err != nil {
		log.Error("New vmCreate error:", err)
		return false, err
	}
	// 查询 创建vm的操作系统对应的vmId
	updateTranOptsNonce(0)
	// 调用合约抢占任务
	task, err := vmCreate.SnatchTask(tranOpts, vmCfg.TaskId, vmCfg.Owner)
	if err != nil {
		log.Error("SnatchTask error:", err)
		return false, err
	}
	log.Info("SnatchTask success:", task.Hash())

	ticket, err := pveApi.GetCreateVmTicket()
	if err != nil {
		return false, err
	}
	vmId, newId := getCloneVmInfo(vmCfg, ticket)
	if vmId == "" || newId == "" {
		return false, nil
	}
	user, password := generateUser()
	vm := &models.VM{
		VmId:       vmId,
		NewId:      newId,
		VmCfg:      vmCfg,
		Name:       fmt.Sprintf("Clone%s", newId),
		User:       user,
		Password:   password,
		HeaderInfo: ticket,
	}
	updateTranOptsNonce(1)
	updateVmProgress(vmCreate, vm.VmCfg.TaskId, big.NewInt(1), bigTwenty)

	// 创建VM、并且设置配置
	_, err = pveApi.CreateVm(vm)
	if err != nil {
		return false, err
	}
	setRes, err := pveApi.SetVmConfig(vm)
	if err != nil || setRes == nil {
		return false, err
	}
	updateTranOptsNonce(1)
	updateVmProgress(vmCreate, vm.VmCfg.TaskId, big.NewInt(2), bigThirty)

	// 开启虚拟机
	_, err = pveApi.StartVm(vm)
	if err != nil {
		return false, err
	}
	updateTranOptsNonce(1)
	updateVmProgress(vmCreate, vm.VmCfg.TaskId, big.NewInt(3), bigForty)

	// 轮训查询状态
	for {
		vmStatus, err := pveApi.VmStatus(vm)
		if err != nil {
			return false, err
		}
		if vmStatus.Status == hashrateCommon.Running {
			updateTranOptsNonce(1)
			updateVmProgress(vmCreate, vm.VmCfg.TaskId, big.NewInt(4), bigSixty)
			break
		}
		time.Sleep(time.Second * 2)
	}
	time.Sleep(time.Second * 20)
	netWorks, err := pveApi.GetVmNetWork(vm)
	if err != nil {
		return false, err
	}

	//loginUser, err := pveApi.SetVmLoginUser(vm)
	//if err != nil {
	//	return false, err
	//}

	updateVmNetWorkInfo(client, netWorks, vm)
	updateVmProgress(vmCreate, vm.VmCfg.TaskId, big.NewInt(5), bighundred)
	return true, nil
}

// updateVmNetWorkInfo  更新vm的网络信息
func updateVmNetWorkInfo(client *ethclient.Client, networkInfos []*models.NetworkInfo, vmInfo *models.VM) {
	internalIp := ""
	for _, netWork := range networkInfos {
		if netWork.Name == hashrateCommon.Ens18 {
			for _, ipInfo := range netWork.IpAddresses {
				if ipInfo.IpAddressType == hashrateCommon.Ipv4 {
					internalIp = ipInfo.IpAddress
					log.Info("internalIp:", internalIp)
					break
				}
			}
		}
	}
	if internalIp == "" {
		log.Error("In networkInfo is not found ip info")
		return
	}
	vmContract, err := vm.NewVm(common.HexToAddress(hashrateCommon.VmContract), client)
	if err != nil {
		return
	}
	// 调用NPS 映射外网ip
	externalIp := internalIp
	externalPort := "22"
	vmId := new(big.Int)
	vmId.SetString(vmInfo.NewId, 10)

	updateTranOptsNonce(1)
	addVirtualMachine, err := vmContract.AddVirtualMachine(tranOpts, vmInfo.VmCfg.Owner, vmInfo.VmCfg.TaskId, vmId, "", vmInfo.VmCfg.TaskId, hostName, vmInfo.Name, "ubuntu")
	if err != nil {
		return
	}
	log.Info("Add virtual machine success:", addVirtualMachine.Hash())

	updateTranOptsNonce(1)

	updateVmNetworkInfo, err := vmContract.UpdateVmNetworkInfo(tranOpts, vmId, "", externalIp, externalPort, "", "", "")
	if err != nil {
		return
	}
	log.Info("Update virtual machine  networkInfo success:", updateVmNetworkInfo.Hash())

	updateTranOptsNonce(1)
	updateVMInfoEvent, err := vmContract.UpdateVMInfoEvent(tranOpts, vmId, vmInfo.VmCfg.Sockets.Uint64(), vmInfo.VmCfg.Memory.Uint64(), vmInfo.VmCfg.GpuNum.Uint64(), vmInfo.VmCfg.Disk)
	if err != nil {
		return
	}
	log.Info("Update virtual machine info success:", updateVMInfoEvent.Hash())

	updateTranOptsNonce(1)
	userTran, err := vmContract.AddVmUser(tranOpts, vmId, vmInfo.User, vmInfo.Password)
	if err != nil {
		log.Error("Add vm user error:", err)
		return
	}
	log.Info("Update virtual machine info success:", userTran.Hash())
}

func generateUser() (string, string) {
	return "cloud", "123456"
}

// getCloneVmInfo 获取能够克隆的vmId
func getCloneVmInfo(vmInfo *models.VmConfig, headerInfo *models.HeaderInfo) (string, string) {
	qemuList, err := pveApi.GetQemuList(headerInfo)
	if err != nil {
		return "", ""
	}
	var maxVmId int64
	for _, qemu := range qemuList {
		if qemu.VmId > maxVmId {
			maxVmId = qemu.VmId
		}
	}
	maxVmId++
	return "104", strconv.FormatInt(maxVmId, 10)
}

// updateVmProgress  更新vm的创建进度
func updateVmProgress(contractInstance *vmCreate.VmCreate, taskId string, stage *big.Int, progress *big.Int) bool {
	task, err := contractInstance.UpdateVmCreateProgress(tranOpts, taskId, stage, progress)
	if err != nil {
		log.Error("UpdateVmCreateProgress error:", err)
		return false
	}
	log.Info("Update vm create progress success:", task.Hash())
	return true
}

func updateTranOptsNonce(incrementNum uint64) {
	nonce += incrementNum
	nonceUint64 := new(big.Int).SetUint64(nonce)
	tranOpts.Nonce = nonceUint64
}
