// Copyright 2019 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 v1

import (
	placementv1alpha1 "github.com/open-cluster-management/multicloud-operators-placementrule/pkg/apis/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

var (
	// AnnotationRollingUpdateMaxUnavailable defines max un available clusters during rolling update.
	AnnotationRollingUpdateMaxUnavailable = SchemeGroupVersion.Group + "/rollingupdate-maxunavaialble"
	// AnnotationRollingUpdateTarget target deployable to rolling update to.
	AnnotationRollingUpdateTarget = SchemeGroupVersion.Group + "/rollingupdate-target"
	// AnnotationDeployableVersion sits in deployable resource to identify if it is local deployable.
	AnnotationDeployableVersion = SchemeGroupVersion.Group + "/deployable-version"
	// AnnotationShared sits in deployable resource to identify if it is local deployable.
	AnnotationShared = SchemeGroupVersion.Group + "/is-shared-deployable"
	// AnnotationLocal sits in deployable resource to identify if it is local deployable.
	AnnotationLocal = SchemeGroupVersion.Group + "/is-local-deployable"
	// AnnotationHosting sits in templated resource, gives name of hosting deployable.
	AnnotationHosting = SchemeGroupVersion.Group + "/" + PropertyHostingDeployable
	// AnnotationManagedCluster identifies this is a deployable for managed cluster.
	AnnotationManagedCluster = SchemeGroupVersion.Group + "/managed-cluster"
	// AnnotationExternalSource identifies this is a deployable created from an external source.
	AnnotationExternalSource = SchemeGroupVersion.Group + "/external-source"
	// AnnotationSubscription identifies this is a object created from Subscription controller.
	// The annotation doesn't exist if the object is created from a Deployable controller.
	AnnotationSubscription = SchemeGroupVersion.Group + "/hosting-subscription"
	// AnnotationIsGenerated tells if the deployable is generated by controller or not.
	AnnotationIsGenerated = SchemeGroupVersion.Group + "/is-generated"
	// LabelSubscriptionPause sits in deployable label to identify if the deployable is paused.
	LabelSubscriptionPause = "subscription-pause"
)

const (
	// DefaultRollingUpdateMaxUnavailablePercentage defines the percentage for rolling update.
	DefaultRollingUpdateMaxUnavailablePercentage = 25
)

var (
	// PropertyHostingDeployable tells NamespacedName of the hosting deployable of the dependency.
	PropertyHostingDeployable = "hosting-deployable"
	// PropertyHostingDeployableName tells the hosting deployable of the dependency.
	PropertyHostingDeployableName = "hosting-deployable-name"
)

// Dependency of Deployable
// Properties field is the flexiblity for different Kind.
type Dependency struct {
	corev1.ObjectReference `json:",inline"`
	Annotations            map[string]string `json:"annotations,omitempty"`
	Labels                 map[string]string `json:"labels,omitempty"`
}

// Cluster is object with name field only to specify cluster.
type Cluster struct {
	Name string `json:"name,omitempty"`
}

// ClusterOverride describes rules for override.
type ClusterOverride struct {
	runtime.RawExtension `json:",inline"`
}

// Overrides field in deployable.
type Overrides struct {
	ClusterName string `json:"clusterName"`
	//+kubebuilder:validation:MinItems=1
	ClusterOverrides []ClusterOverride `json:"clusterOverrides"` // To be added
}

// DeployableSpec defines the desired state of Deployable.
type DeployableSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
	// Important: Run "make" to regenerate code after modifying this file

	Template     *runtime.RawExtension        `json:"template"`
	Dependencies []Dependency                 `json:"dependencies,omitempty"`
	Placement    *placementv1alpha1.Placement `json:"placement,omitempty"`
	Overrides    []Overrides                  `json:"overrides,omitempty"`
	Channels     []string                     `json:"channels,omitempty"`
}

// DeployablePhase indicate the phase of a deployable.
type DeployablePhase string

const (
	// DeployableUnknown means not processed by controller yet.
	DeployableUnknown DeployablePhase = ""
	// DeployableDeployed means successfully deployed to remote clusters.
	DeployableDeployed DeployablePhase = "Deployed"
	// DeployableFailed means failed to apply the template into system.
	DeployableFailed DeployablePhase = "Failed"
	// DeployablePropagated means created other managed deployables.
	DeployablePropagated DeployablePhase = "Propagated"
)

// ResourceUnitStatus aggregates status from target clusters.
type ResourceUnitStatus struct {
	Phase          DeployablePhase `json:"phase"`
	Reason         string          `json:"reason,omitempty"`
	Message        string          `json:"message,omitempty"`
	LastUpdateTime *metav1.Time    `json:"lastUpdateTime,omitempty"`

	ResourceStatus *runtime.RawExtension `json:"resourceStatus,omitempty"`
}

// DeployableStatus defines the observed state of Deployable.
type DeployableStatus struct {
	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
	// Important: Run "make" to regenerate code after modifying this file
	ResourceUnitStatus `json:",inline"`
	PropagatedStatus   map[string]*ResourceUnitStatus `json:"targetClusters,omitempty"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Deployable is the Schema for the deployables API
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="template-kind",type="string",JSONPath=".spec.template.kind",description="Kind of the template"
// +kubebuilder:printcolumn:name="template-apiversion",type="string",JSONPath=".spec.template.apiVersion",description="api version of the template"
// +kubebuilder:printcolumn:name="age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.phase"
type Deployable struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   DeployableSpec   `json:"spec"`
	Status DeployableStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// DeployableList contains a list of Deployable.
type DeployableList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`
	Items           []Deployable `json:"items"`
}

func init() {
	SchemeBuilder.Register(&Deployable{}, &DeployableList{})
}
