/*
Copyright 2022.

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 (
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	ResourceName = "policies"
	Kind         = "Policy"
)

var GroupVersionResource = GroupVersion.WithResource(ResourceName)

// PolicyParameters defines a needed input in a policy
type PolicyParameters struct {
	// Name is a descriptive name of a policy parameter
	Name string `json:"name"`
	// Type is the type of that parameter, integer, string,...
	Type string `json:"type"`
	// Required specifies if this is a necessary value or not
	Required bool `json:"required"`
	// +optional
	// Value is the value for that parameter
	Value *apiextensionsv1.JSON `json:"value,omitempty"`
}

// PolicyTargets are filters used to determine which resources should be evaluated against a policy
type PolicyTargets struct {
	// Kinds is a list of Kubernetes kinds that are supported by this policy
	Kinds []string `json:"kinds"`
	// +optional
	// Labels is a list of Kubernetes labels that are needed to evaluate the policy against a resource
	// this filter is statisfied if only one label existed, using * for value make it so it will match if the key exists regardless of its value
	Labels []map[string]string `json:"labels"`
	// +optional
	// Namespaces is a list of Kubernetes namespaces that a resource needs to be a part of to evaluate against this policy
	Namespaces []string `json:"namespaces"`
}

// PolicySpec defines the desired state of Policy
// It describes all that is needed to evaluate a resource against a rego code
// +kubebuilder:object:generate:true
type PolicySpec struct {
	// Name is the policy name
	Name string `json:"name"`
	// ID is the policy unique identifier
	ID string `json:"id"`
	// Code contains the policy rego code
	Code string `json:"code"`
	// +optional
	// Enable specifies if this policy should be used for evaluation or not
	Enable string `json:"enable,omitempty"`
	// +optional
	// Parameters are the inputs needed for the policy validation
	Parameters []PolicyParameters `json:"parameters,omitempty"`
	// +optional
	// Targets describes the required metadata that needs to be matched to evaluate a resource against the policy
	// all values specified need to exist in the resource to be considered for evaluation
	Targets PolicyTargets `json:"targets,omitempty"`
	// Description is a summary of what that policy validates
	Description string `json:"description"`
	// HowToSolve is a description of the steps required to solve the issues reported by the policy
	HowToSolve string `json:"how_to_solve"`
	// Category specifies under which grouping this policy should be included
	Category string `json:"category"`
	// +optional
	// Tags is a list of tags associated with that policy
	Tags []string `json:"tags,omitempty"`
	// +kubebuilder:validation:Enum=low;medium;high
	// Severity is a measure of the impact of that policy, can be low, medium or high
	Severity string `json:"severity"`
	// +optional
	// Controls is a list of policy controls that this policy falls under
	Controls []string `json:"controls,omitempty"`
}

//+kubebuilder:unservedversion
//+kubebuilder:object:root=true
//+kubebuilder:resource:scope=Cluster

// Policy is the Schema for the policies API
type Policy struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec PolicySpec `json:"spec,omitempty"`
}

// +kubebuilder:unservedversion
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster

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

func init() {
	SchemeBuilder.Register(&Policy{}, &PolicyList{})
}
