// Copyright 2017 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.

/*
Copyright 2016 The Kubernetes Authors.

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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resourcelock

import (
	"encoding/json"
	"errors"
	"fmt"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
)

// ProvisionPVCLock is a lock on an existing PVC to provision a PV for
type ProvisionPVCLock struct {
	// PVCMeta should contain a Name and a Namespace of a PVC
	// object that the LeaderElector will attempt to lead.
	PVCMeta    metav1.ObjectMeta
	Client     clientset.Interface
	LockConfig Config
	p          *v1.PersistentVolumeClaim
}

// Get returns the LeaderElectionRecord
func (pl *ProvisionPVCLock) Get() (*LeaderElectionRecord, error) {
	var record LeaderElectionRecord
	var err error
	pl.p, err = pl.Client.Core().PersistentVolumeClaims(pl.PVCMeta.Namespace).Get(pl.PVCMeta.Name, metav1.GetOptions{})
	if err != nil {
		return nil, err
	}
	// TODO there should be a way to give up if the pvc is already bound...we are doing a Get regardless
	if pl.p.Annotations == nil {
		pl.p.Annotations = make(map[string]string)
	}
	if recordBytes, found := pl.p.Annotations[LeaderElectionRecordAnnotationKey]; found {
		if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
			return nil, err
		}
	}
	return &record, nil
}

// Create is not allowed, the PVC should already exist
func (pl *ProvisionPVCLock) Create(ler LeaderElectionRecord) error {
	return errors.New("create not allowed, PVC should already exist")
}

// Update will update and existing annotation on a given resource.
func (pl *ProvisionPVCLock) Update(ler LeaderElectionRecord) error {
	if pl.p == nil {
		return errors.New("PVC not initialized, call get first")
	}
	recordBytes, err := json.Marshal(ler)
	if err != nil {
		return err
	}
	pl.p.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes)
	pl.p, err = pl.Client.Core().PersistentVolumeClaims(pl.PVCMeta.Namespace).Update(pl.p)
	return err
}

// RecordEvent in leader election while adding meta-data
func (pl *ProvisionPVCLock) RecordEvent(s string) {
	// events := fmt.Sprintf("%v %v", pl.LockConfig.Identity, s)
	// pl.LockConfig.EventRecorder.Eventf(&v1.PersistentVolumeClaim{ObjectMeta: pl.p.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events)
}

// Describe is used to convert details on current resource lock
// into a string
func (pl *ProvisionPVCLock) Describe() string {
	return fmt.Sprintf("to provision for pvc %v/%v", pl.PVCMeta.Namespace, pl.PVCMeta.Name)
}

// Identity returns the Identity of the lock
func (pl *ProvisionPVCLock) Identity() string {
	return pl.LockConfig.Identity
}
