package main

import (
	"os"

	dstore "github.com/ipfs/go-datastore"
	"github.com/mitchellh/go-homedir"
	"github.com/urfave/cli/v2"
	"golang.org/x/xerrors"
	"gopkg.in/cheggaaa/pb.v1"

	"github.com/filecoin-project/go-jsonrpc"

	"github.com/filecoin-project/lotus/chain/store"
	lcli "github.com/filecoin-project/lotus/cli"
	"github.com/filecoin-project/lotus/lib/backupds"
	"github.com/filecoin-project/lotus/node/config"
	"github.com/filecoin-project/lotus/node/repo"
)

var backupCmd = lcli.BackupCmd("repo", repo.FullNode, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
	return lcli.GetFullNodeAPI(cctx)
})

func restore(cctx *cli.Context, r repo.Repo) error {
	bf, err := homedir.Expand(cctx.Path("restore"))
	if err != nil {
		return xerrors.Errorf("expand backup file path: %w", err)
	}

	st, err := os.Stat(bf)
	if err != nil {
		return xerrors.Errorf("stat backup file (%s): %w", bf, err)
	}

	f, err := os.Open(bf)
	if err != nil {
		return xerrors.Errorf("opening backup file: %w", err)
	}
	defer f.Close() // nolint:errcheck

	lr, err := r.Lock(repo.FullNode)
	if err != nil {
		return err
	}
	defer lr.Close() // nolint:errcheck

	if cctx.IsSet("restore-config") {
		log.Info("Restoring config")

		cf, err := homedir.Expand(cctx.String("restore-config"))
		if err != nil {
			return xerrors.Errorf("expanding config path: %w", err)
		}

		_, err = os.Stat(cf)
		if err != nil {
			return xerrors.Errorf("stat config file (%s): %w", cf, err)
		}

		var cerr error
		err = lr.SetConfig(func(raw interface{}) {
			rcfg, ok := raw.(*config.FullNode)
			if !ok {
				cerr = xerrors.New("expected miner config")
				return
			}

			ff, err := config.FromFile(cf, config.SetDefault(func() (interface{}, error) { return rcfg, nil }))
			if err != nil {
				cerr = xerrors.Errorf("loading config: %w", err)
				return
			}

			*rcfg = *ff.(*config.FullNode)
		})
		if cerr != nil {
			return cerr
		}
		if err != nil {
			return xerrors.Errorf("setting config: %w", err)
		}

	} else {
		log.Warn("--restore-config NOT SET, WILL USE DEFAULT VALUES")
	}

	log.Info("Restoring metadata backup")

	mds, err := lr.Datastore(cctx.Context, "/metadata")
	if err != nil {
		return err
	}

	bar := pb.New64(st.Size())
	br := bar.NewProxyReader(f)
	bar.ShowTimeLeft = true
	bar.ShowPercent = true
	bar.ShowSpeed = true
	bar.Units = pb.U_BYTES

	bar.Start()
	err = backupds.RestoreInto(br, mds)
	bar.Finish()

	if err != nil {
		return xerrors.Errorf("restoring metadata: %w", err)
	}

	log.Info("Resetting chainstore metadata")

	chainHead := dstore.NewKey("head")
	if err := mds.Delete(cctx.Context, chainHead); err != nil {
		return xerrors.Errorf("clearing chain head: %w", err)
	}
	if err := store.FlushValidationCache(cctx.Context, mds); err != nil {
		return xerrors.Errorf("clearing chain validation cache: %w", err)
	}

	return nil
}
