// Copyright 2019 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package restore

import (
	"database/sql"
	"path/filepath"

	"github.com/pingcap/tep/cmd/tep-metadata-backup-manager/app/constants"
	"github.com/pingcap/tep/cmd/tep-metadata-backup-manager/app/util"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	glog "k8s.io/klog"
)

// RestoreManager mainly used to manage backup related work
type RestoreManager struct {
	kubernetes.Interface
	RestoreOpts
}

// NewRestoreManager return a RestoreManager
func NewRestoreManager(kubeCli kubernetes.Interface, backupOpts RestoreOpts) *RestoreManager {
	return &RestoreManager{
		kubeCli,
		backupOpts,
	}
}

// ProcessRestore used to process the restore logic
func (rm *RestoreManager) ProcessRestore() error {
	var err error

	err = wait.PollImmediate(constants.PollInterval, constants.CheckTimeout, func() (done bool, err error) {
		var db *sql.DB
		db, err = util.OpenDB(rm.getDSN(constants.TidbMetaDB))
		if err != nil {
			glog.Warningf("can't open connection to tidb cluster %s, err: %v", rm, err)
			return false, nil
		}

		if err = db.Ping(); err != nil {
			glog.Warningf("can't connect to tidb cluster %s, err: %s", rm, err)
			return false, nil
		}
		db.Close()
		return true, nil
	})

	if err != nil {
		glog.Errorf("cluster %s connect failed, err: %s", rm, err)
		return err
	}

	return rm.performRestore()
}

func (rm *RestoreManager) performRestore() error {
	var err error

	restoreDataPath := rm.getRestoreDataPath()
	if err := rm.downloadBackupData(restoreDataPath); err != nil {
		glog.Errorf("download cluster %s backup %s data failed, err: %s", rm, rm.BackupPath, err)
		return err
	}
	glog.Infof("download cluster %s backup %s data success", rm, rm.BackupPath)

	restoreDataDir := filepath.Dir(restoreDataPath)
	unarchiveDataPath, err := unarchiveBackupData(restoreDataPath, restoreDataDir)
	if err != nil {
		glog.Errorf("unarchive cluster %s backup %s data failed, err: %s", rm, restoreDataPath, err)
		return err
	}
	glog.Infof("unarchive cluster %s backup %s data success", rm, restoreDataPath)

	err = rm.loadTidbClusterData(unarchiveDataPath)
	if err != nil {
		glog.Errorf("restore cluster %s from backup %s failed, err: %s", rm, rm.BackupPath, err)
		return err
	}
	glog.Infof("restore cluster %s from backup %s success", rm, rm.BackupPath)

	return nil
}
