package app

import (
	"bytes"
	"chain33-pai/pkg/setting"
	"chain33-pai/service/pai_service"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"time"
)

var (
	btyPath string
)

func RaspberryChan() {
	for {
		if value, ok := <-JobChan; ok {
			tlog.Info("raspberrychan", "msg", value)
			if value.Name == "" {
				continue
			}
			path := GetAbsPath(setting.BityuanSetting.Name)
			if path != "" {
				btyPath = path
			}
			switch value.Name {
			case "ROLLBACK":
				err := rollback()
				if err == nil {
					tlog.Info("rollback success", "err", nil)
				} else {
					tlog.Error("rollback fail ", "err", err)
				}
			case "BACKUP":
				err := backup()
				if err == nil {
					tlog.Info("backup success", "err", nil)
				} else {
					tlog.Error("backup fail ", "err", err)
				}
			case "RECOVER":
				err := recoverNode()
				if err == nil {
					tlog.Info("recover success", "err", nil)
				} else {
					tlog.Error("recover fail ", "err", err)
				}
			case "DELETEBACKUP":
				err := deleteBackup()
				if err == nil {
					tlog.Info("deleteBackup success", "err", nil)
				} else {
					tlog.Error("deleteBackup fail ", "err", err)
				}
			case "RESTART":
				err := restartNode()
				if err == nil {
					tlog.Info("deleteBackup success", "err", nil)
				} else {
					tlog.Error("deleteBackup fail ", "err", err)
				}
			}

		}
		time.Sleep(time.Second * 1)
	}
}

func rollback() error {
	BityuanFlag.Lock.Lock()
	defer BityuanFlag.Lock.Unlock()
	BityuanFlag.Flag = true
	_, err := SafeCloseNode()
	if err != nil {
		BityuanFlag.Flag = false
		return err
	}
	var pai pai_service.Pai
	err = pai.SetPai()
	if err != nil {
		tlog.Error("SetPai", "err", err)
		BityuanFlag.Flag = false
		return err
	}
	if pai.LocalLastHeight == 0 {
		tlog.Error("LocalLastHeight", "height", pai.LocalLastHeight)
		BityuanFlag.Flag = false
		return err
	}
	rollheight := pai.LocalLastHeight - 1000
	if (pai.LocalLastHeight - 1000) <= 0 {
		rollheight = 1
	}
	isrun := MakeSureBtyIsNotRun()
	if isrun {
		BityuanFlag.Flag = false
		return errors.New("bty is running")
	}
	tlog.Info("rollback start", "height", pai.LocalLastHeight, "rollbackheight", rollheight)
	var buf bytes.Buffer
	if btyPath == "" {
		tlog.Error("GetAbsPath", "err", "btyPath not exists")
		BityuanFlag.Flag = false
		return err
	}
	cmd := exec.Command(btyPath+"/"+setting.BityuanSetting.Name, "-rollback", fmt.Sprintf("%d", rollheight))
	cmd.Stdout = &buf
	err = cmd.Run()
	if err != nil {
		BityuanFlag.Flag = false
		return err
	}
	tlog.Info("rollback end", "height", pai.LocalLastHeight, "rollbackheight", rollheight)
	BityuanFlag.Flag = false
	return nil
}

func backup() error {
	BityuanFlag.Lock.Lock()
	defer BityuanFlag.Lock.Unlock()
	if BityuanFlag.Flag {
		return errors.New("env err")
	}
	BityuanFlag.Flag = true
	ok, err := SafeCloseNode()
	if err != nil {
		BityuanFlag.Flag = false
		tlog.Error("SafeCloseNode", "err", err)
		return err
	}
	if !ok {
		BityuanFlag.Flag = false
		return errors.New("env err")
	}
	isrun := MakeSureBtyIsNotRun()
	if isrun {
		BityuanFlag.Flag = false
		return errors.New("bty is running")
	}
	err = deleteBackup()
	if err != nil && err != errors.New("backup file not exists") {
		BityuanFlag.Flag = false
		return err
	}
	err = SafeBackup()
	if err != nil {
		return err
	}
	BityuanFlag.Flag = false
	StartProcess(setting.Chain33Pai.Auto)
	return nil
}

func SafeBackup() error {
	s := time.Now()
	defer func() {
		tlog.Info("SafeBackup", "cost", time.Since(s))
	}()
	var buf bytes.Buffer
	backup := exec.Command("cp", "-r", btyPath+"/datadir", btyPath+"/datadir_backup")
	backup.Stderr = &buf
	if err := backup.Run(); err != nil {
		tlog.Error("SafeBackup", "err", buf.String())
		return err
	}
	return nil
}

func recoverNode() error {
	BityuanFlag.Lock.Lock()
	defer BityuanFlag.Lock.Unlock()
	if btyPath == "" {
		tlog.Error("btyPath empty", "err", "node not exists")
		return errors.New("node not exists")
	}
	_, err := SafeCloseNode()
	if err != nil {
		return err
	}
	isrun := MakeSureBtyIsNotRun()
	if isrun {
		return errors.New("bty is running")
	}
	backupPath := btyPath + "/datadir_backup"
	if _, err := os.Stat(backupPath); err != nil {
		if os.IsNotExist(err) {
			tlog.Error("backup file not exists", "err", backupPath)
			return errors.New("backup file not exists")
		}
	}
	remove := exec.Command("rm", "-rf", btyPath+"/datadir")
	err = remove.Run()
	if err != nil {
		tlog.Error("rm -rf datadir", "err", err)
		return err
	}

	recover := exec.Command("cp", "-r", backupPath, btyPath+"/datadir")
	err = recover.Run()
	if err != nil {
		tlog.Error("cp -r datadir_backup datadir", "err", err)
		return err
	}
	StartProcess(setting.Chain33Pai.Auto)
	return nil
}

func deleteBackup() error {
	s := time.Now()
	defer func() {
		tlog.Info("deleteBackup", "cost", time.Since(s))
	}()
	backupPath := btyPath + "/datadir_backup"
	if _, err := os.Stat(backupPath); err != nil {
		if os.IsNotExist(err) {
			tlog.Error("backup file not exists", "err", backupPath)
			return nil
		}
	}
	remove := exec.Command("rm", "-rf", backupPath)
	err := remove.Run()
	if err != nil {
		tlog.Error("rm -rf datadir_backup", "err", err)
		return err
	}
	return nil
}

func restartNode() error {
	BityuanFlag.Lock.Lock()
	defer BityuanFlag.Lock.Unlock()
	if btyPath == "" {
		tlog.Error("btyPath empty", "err", "node not exists")
		return errors.New("node not exists")
	}
	_, err := SafeCloseNode()
	if err != nil {
		return err
	}
	isrun := MakeSureBtyIsNotRun()
	if isrun {
		return errors.New("bty is running")
	}
	StartProcess(setting.Chain33Pai.Auto)
	return nil
}

func MakeSureBtyIsNotRun() bool {
	s := time.Now()
	defer func() {
		tlog.Info("MakeSureBtyIsNotRun", "cost", time.Since(s))
	}()
	num := 0
	for {
		if num > 1000 {
			break
		}
		info := getProcessInfo(setting.BityuanSetting.Name)
		if info == "" {
			return false
		}
		num++
		time.Sleep(time.Second * 1)
	}
	return true
}
