/*
Copyright 2018 Pressinfra SRL.

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 v1alpha1

import (
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// SecretRef represents a reference to a Secret.
type SecretRef string

// Domain represents a valid domain name.
type Domain string

// RouteSpec defines a desired state for a route.
type RouteSpec struct {
	// Domain for the route
	// +kubebuilder:validation:MinLength=1
	Domain string `json:"domain"`
	// The path for the route. Defaults to /.
	// +optional
	Path string `json:"path"`
}

// WordpressConditionType defines condition types of a backup resources.
type WordpressConditionType string

// WordpressCondition defines condition struct for backup resource.
type WordpressCondition struct {
	// Type of Wordpress condition.
	Type WordpressConditionType `json:"type"`
	// Status of the condition, one of True, False, Unknown.
	Status corev1.ConditionStatus `json:"status"`
	// The last time this condition was updated.
	LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
	// Last time the condition transitioned from one status to another.
	LastTransitionTime metav1.Time `json:"lastTransitionTime"`
	// The reason for the condition's last transition.
	Reason string `json:"reason"`
	// A human readable message indicating details about the transition.
	Message string `json:"message"`
}

const (
	// WPCronTriggeringCondition signals that health of wp-cron trigger.
	WPCronTriggeringCondition WordpressConditionType = "WPCronTriggering"

	// WPCronTriggerErrorReason is the generic reason for cron failures.
	WPCronTriggerErrorReason = "WPCronTriggerError"

	// WPCronTriggeringReason is the reason for successfully triggering wp-cron.
	WPCronTriggeringReason = "WPCronTriggering"
)

// WordpressSpec defines the desired state of Wordpress.
type WordpressSpec struct {
	// Number of desired web pods. This is a pointer to distinguish between
	// explicit zero and not specified. Defaults to 1.
	// +optional
	Replicas *int32 `json:"replicas,omitempty"`
	// Domains for which this this site answers.
	// The first item is set as the "main domain" (eg. WP_HOME and WP_SITEURL constants).
	// Deprecated: use Routes instead. This field will be dropped in next release.
	// +optional
	Domains []Domain `json:"domains,omitempty"`
	// Routes for which the ingress is created
	// The first item is set the WP_HOME and WP_SITEURL constants.
	// If no routes are specified, ingress syncing is disabled and WP_HOME de defaults to NAME.NAMESPACE.svc.
	// +optional
	Routes []RouteSpec `json:"routes,omitempty"`
	// WordPress runtime image to use. Defaults to docker.io/bitpoke/wordpress-runtime:<latest stable runtime tag>
	// +optional
	Image string `json:"image,omitempty"`
	// ImagePullPolicy overrides WordpressRuntime spec.imagePullPolicy
	// +kubebuilder:validation:Enum=Always;IfNotPresent;Never
	// +optional
	ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
	// ImagePullSecrets defines additional secrets to use when pulling images
	ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
	// ServiceAccountName is the name of the ServiceAccount to use to run this
	// site's pods
	// More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
	// +optional
	ServiceAccountName string `json:"serviceAccountName,omitempty"`
	// TLSSecretRef a secret containing the TLS certificates for this site.
	// +optional
	TLSSecretRef SecretRef `json:"tlsSecretRef,omitempty"`
	// DeploymentStrategy allows setting the deployment strategy for the WordPress site
	DeploymentStrategy *appsv1.DeploymentStrategy `json:"deploymentStrategy,omitempty"`
	// CodeVolumeSpec specifies how the site's code gets mounted into the
	// container. If not specified, a code volume won't get mounted at all.
	// +optional
	CodeVolumeSpec *CodeVolumeSpec `json:"code,omitempty"`
	// MediaVolumeSpec specifies how media files get mounted into the runtime
	// container. If not specified, a media volume won't be mounted at all.
	// +optional
	MediaVolumeSpec *MediaVolumeSpec `json:"media,omitempty"`
	// Volumes defines additional volumes to get injected into web and cli pods
	// +optional
	Volumes []corev1.Volume `json:"volumes,omitempty"`
	// PodMetadata allow setting custom labels/annotations on wordpress pods
	// +optional
	PodMetadata *metav1.ObjectMeta `json:"podMetadata,omitempty"`
	// ReadinessProbe allows setting a custom readiness probe for the wordpress container.
	// If not specified, a default probe that makes a HTTP request on the "/" path will be used.
	// +optional
	ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"`
	// LivenessProbe allows setting a custom liveness probe for the wordpress container.
	// If not specified, a default probe that makes a HTTP request on the "/-/php-ping" path will be used.
	// +optional
	LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"`
	// WordpressBootstrapSpec specifies credentials used to install wordpress, on the first run.
	// +optional
	WordpressBootstrapSpec *WordpressBootstrapSpec `json:"bootstrap,omitempty"`
	// WordpressPathPrefix is the path prefix under which wordpress is available.
	// It defaults to /wp.
	// +optional
	WordpressPathPrefix string `json:"wordpressPathPrefix,omitempty"`
	// VolumeMountsSpec defines additional mounts which get injected into web
	// and cli pods.
	// +optional
	VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
	// Env defines environment variables which get passed into web and cli pods
	// +optional
	// +patchMergeKey=name
	// +patchStrategy=merge
	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
	// EnvFrom defines envFrom's which get passed into web and cli containers
	// +optional
	EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
	// If specified, the resources required by wordpress container.
	// More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
	// +optional
	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
	// If specified, Pod node selector
	// +optional
	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
	// If specified, the pod's tolerations.
	// +optional
	Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
	// If specified, the pod's scheduling constraints
	// +optional
	Affinity *corev1.Affinity `json:"affinity,omitempty"`
	// If specified, indicates the pod's priority class
	// +optional
	PriorityClassName string `json:"priorityClassName,omitempty"`
	// IngressAnnotations for this Wordpress site
	// +optional
	IngressAnnotations map[string]string `json:"ingressAnnotations,omitempty"`
	// Additional init containers
	// +optional
	InitContainers []corev1.Container `json:"initContainers,omitempty"`
	// Additional sidecar containers (eg. blackfire or tideways agent)
	// +optional
	Sidecars []corev1.Container `json:"sidecars,omitempty"`
}

// GitVolumeSource is the desired spec for git code source.
type GitVolumeSource struct {
	// Repository is the git repository for the code
	Repository string `json:"repository"`
	// GitRef to clone (can be a branch name, but it should point to a tag or a
	// commit hash)
	// +optional
	GitRef string `json:"reference,omitempty"`
	// Env defines env variables  which get passed to the git clone container
	// +optional
	// +patchMergeKey=name
	// +patchStrategy=merge
	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
	// EnvFrom defines envFrom which get passed to the git clone container
	// +optional
	EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
	// EmptyDir volume to use for git cloning.
	// +optional
	EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}

// S3VolumeSource is the desired spec for accessing media files over S3
// compatible object store.
type S3VolumeSource struct {
	// Bucket for storing media files
	// +kubebuilder:validation:MinLength=1
	Bucket string `json:"bucket"`
	// PathPrefix is the prefix for media files in bucket
	PathPrefix string `json:"prefix,omitempty"`
	// Env variables for accessing S3 bucket. Taken into account are:
	// ACCESS_KEY, SECRET_ACCESS_KEY
	// +optional
	// +patchMergeKey=name
	// +patchStrategy=merge
	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
}

// GCSVolumeSource is the desired spec for accessing media files using google
// cloud storage object store.
type GCSVolumeSource struct {
	// Bucket for storing media files
	// +kubebuilder:validation:MinLength=1
	Bucket string `json:"bucket"`
	// PathPrefix is the prefix for media files in bucket
	PathPrefix string `json:"prefix,omitempty"`
	// Env variables for accessing gcs bucket. Taken into account are:
	// GOOGLE_APPLICATION_CREDENTIALS_JSON
	// +optional
	// +patchMergeKey=name
	// +patchStrategy=merge
	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
}

// CodeVolumeSpec is the desired spec for mounting code into the wordpress
// runtime container.
type CodeVolumeSpec struct {
	// Metadata for the media volume. Currently only labels and annotations are set if a PVC is specified
	metav1.ObjectMeta `json:"metadata,omitempty"`
	// ReadOnly specifies if the volume should be mounted read-only inside the
	// wordpress runtime container
	ReadOnly bool `json:"readOnly,omitempty"`
	// MountPath specifies where should the code volume be mounted.
	// Defaults to /app/web/wp-content
	// +optional
	MountPath string `json:"mountPath,omitempty"`
	// ContentSubPath specifies where within the code volumes, the WP_CONTENT_DIR is located.
	// Defaults to: wp-content
	// +optional
	ContentSubPath string `json:"contentSubPath,omitempty"`
	// ConfigSubPath specifies where within the code volumes the config directory is located.
	// Defaults to: config
	// +optional
	ConfigSubPath string `json:"configSubPath,omitempty"`
	// GitDir specifies the git repo to use for code cloning. It has the highest
	// level of precedence over EmptyDir, HostPath and PersistentVolumeClaim
	// +optional
	GitDir *GitVolumeSource `json:"git,omitempty"`
	// PersistentVolumeClaim to use if no GitDir is specified
	// +optional
	PersistentVolumeClaim *corev1.PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"`
	// HostPath to use if no PersistentVolumeClaim is specified
	// +optional
	HostPath *corev1.HostPathVolumeSource `json:"hostPath,omitempty"`
	// EmptyDir to use if no HostPath is specified
	// +optional
	EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}

// MediaVolumeSpec is the desired spec for handling media files at runtime.
type MediaVolumeSpec struct {
	// Metadata for the media volume. Currently only labels and annotations are set if a PVC is specified
	metav1.ObjectMeta `json:"metadata,omitempty"`
	// ReadOnly specifies if the volume should be mounted read-only inside the
	// wordpress runtime container
	ReadOnly bool `json:"readOnly,omitempty"`
	// MountPath specifies where should the media volume be mounted.
	// Defaults to '/uploads' folder within the CodeVolumeSpec.MountPath
	// +optional
	MountPath string `json:"mountPath,omitempty"`
	// ContentSubPath specifies where within the media volume, the media files are located.
	// +optional
	ContentSubPath string `json:"contentSubPath,omitempty"`
	// S3VolumeSource specifies the S3 object storage configuration for media
	// files. It has the highest level of precedence over EmptyDir, HostPath
	// and PersistentVolumeClaim
	// +optional
	S3VolumeSource *S3VolumeSource `json:"s3,omitempty"`
	// GCSVolumeSource specifies the google cloud storage object storage
	// configuration for media files. It has the highest level of precedence
	// over EmptyDir, HostPath and PersistentVolumeClaim
	// +optional
	GCSVolumeSource *GCSVolumeSource `json:"gcs,omitempty"`
	// PersistentVolumeClaim to use if no S3VolumeSource or GCSVolumeSource are
	// specified
	// +optional
	PersistentVolumeClaim *corev1.PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"`
	// HostPath to use if no PersistentVolumeClaim is specified
	// +optional
	HostPath *corev1.HostPathVolumeSource `json:"hostPath,omitempty"`
	// EmptyDir to use if no HostPath is specified
	// +optional
	EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}

// WordpressBootstrapSpec requires defining at least.
// `WORDPRESS_BOOSTRAP_USER` and `WORDPRESS_BOOTSTRAP_PASSWORD` env variables.
// `WORDPRESS_BOOSTRAP_EMAIL` and `WORDPRESS_BOOTSTRAP_TITLE` are also used if provided.
type WordpressBootstrapSpec struct {
	// Env defines environment variables for bootstrapping WordPress
	// +optional
	// +patchMergeKey=name
	// +patchStrategy=merge
	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
	// EnvFrom defines envFrom's which get passed into wordpress bootstrapper
	// +optional
	EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"`
}

// WordpressStatus defines the observed state of Wordpress.
type WordpressStatus struct {
	// Conditions represents the Wordpress resource conditions list.
	// +optional
	Conditions []WordpressCondition `json:"conditions,omitempty"`
	// Total number of non-terminated pods targeted by web deployment
	// This is copied over from the deployment object
	// +optional
	Replicas int32 `json:"replicas,omitempty"`
}

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

// Wordpress is the Schema for the wordpresses API.
// +k8s:openapi-gen=true
// +kubebuilder:resource:shortName=wp
// +kubebuilder:subresource:status
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas
// +kubebuilder:printcolumn:name="image",type="string",JSONPath=".spec.image",description="wordpress image"
// +kubebuilder:printcolumn:name="wp-cron",type="string",JSONPath=".status.conditions[?(@.type == 'WPCronTriggering')].status",description="wp-cron triggering status"
type Wordpress struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   WordpressSpec   `json:"spec,omitempty"`
	Status WordpressStatus `json:"status,omitempty"`
}

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

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

func init() {
	SchemeBuilder.Register(&Wordpress{}, &WordpressList{})
}
