// 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 (
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"github.com/pingcap/tidb-operator/pkg/backup"
	informers "github.com/pingcap/tidb-operator/pkg/client/informers/externalversions/pingcap/v1alpha1"
	"github.com/pingcap/tidb-operator/pkg/controller"
	"k8s.io/client-go/tools/cache"
)

// ControlInterface implements the control logic for updating Restore
// It is implemented as an interface to allow for extensions that provide different semantics.
// Currently, there is only one implementation.
type ControlInterface interface {
	// UpdateRestore implements the control logic for restore job creation, update, and deletion
	UpdateRestore(restore *v1alpha1.Restore) error
	// UpdateCondition updates the condition for a Restore.
	UpdateCondition(restore *v1alpha1.Restore, condition *v1alpha1.RestoreCondition) error
}

// NewDefaultRestoreControl returns a new instance of the default implementation RestoreControlInterface that
// implements the documented semantics for Restore.
func NewDefaultRestoreControl(restoreManager backup.RestoreManager) ControlInterface {
	return &defaultRestoreControl{
		restoreManager,
	}
}

type defaultRestoreControl struct {
	restoreManager backup.RestoreManager
}

var _ ControlInterface = &defaultRestoreControl{}

// UpdateRestore executes the core logic loop for a Restore.
func (c *defaultRestoreControl) UpdateRestore(restore *v1alpha1.Restore) error {
	restore.SetGroupVersionKind(controller.RestoreControllerKind)
	return c.restoreManager.Sync(restore)
}

// UpdateCondition updates the condition for a Restore.
func (c *defaultRestoreControl) UpdateCondition(restore *v1alpha1.Restore, condition *v1alpha1.RestoreCondition) error {
	return c.restoreManager.UpdateCondition(restore, condition)
}

// FakeRestoreControl is a fake RestoreControlInterface
type FakeRestoreControl struct {
	backupIndexer        cache.Indexer
	updateRestoreTracker controller.RequestTracker
	condition            *v1alpha1.RestoreCondition
}

// NewFakeRestoreControl returns a FakeRestoreControl
func NewFakeRestoreControl(restoreInformer informers.RestoreInformer) *FakeRestoreControl {
	return &FakeRestoreControl{
		restoreInformer.Informer().GetIndexer(),
		controller.RequestTracker{},
		nil,
	}
}

// SetUpdateRestoreError sets the error attributes of updateRestoreTracker
func (c *FakeRestoreControl) SetUpdateRestoreError(err error, after int) {
	c.updateRestoreTracker.SetError(err).SetAfter(after)
}

// UpdateRestore adds the backup to RestoreIndexer
func (c *FakeRestoreControl) UpdateRestore(backup *v1alpha1.Restore) error {
	defer c.updateRestoreTracker.Inc()
	if c.updateRestoreTracker.ErrorReady() {
		defer c.updateRestoreTracker.Reset()
		return c.updateRestoreTracker.GetError()
	}

	return c.backupIndexer.Add(backup)
}

// UpdateCondition updates the condition for a Restore.
func (c *FakeRestoreControl) UpdateCondition(_ *v1alpha1.Restore, condition *v1alpha1.RestoreCondition) error {
	c.condition = condition
	return nil
}

var _ ControlInterface = &FakeRestoreControl{}
