/*

 Copyright (c) [2023] [pcm]
 [pcm-coordinator] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.

*/

package tracker

import (
	"fmt"
	"strings"
	"time"
)

type Level int

const (
	LevelCluster = 1 << iota
	LevelNode
	LevelWorkspace
	LevelNamespace
	LevelApplication
	LevelController
	LevelPod
	LevelContainer
	LevelPVC
	LevelComponent
	LevelAdapter
)

var MeteringLevelMap = map[string]int{
	"LevelAdapter":     LevelAdapter,
	"LevelCluster":     LevelCluster,
	"LevelNode":        LevelNode,
	"LevelWorkspace":   LevelWorkspace,
	"LevelNamespace":   LevelNamespace,
	"LevelApplication": LevelApplication,
	"LevelController":  LevelController,
	"LevelPod":         LevelPod,
	"LevelContainer":   LevelContainer,
	"LevelPVC":         LevelPVC,
	"LevelComponent":   LevelComponent,
}

type QueryOption interface {
	Apply(*QueryOptions)
}

type MeterOptions struct {
	Start time.Time
	End   time.Time
	Step  time.Duration
}

type QueryOptions struct {
	Level                     Level
	ClustersName              string
	NamespacedResourcesFilter string
	QueryType                 string
	ResourceFilter            string
	ClusterName               string
	NodeName                  string
	WorkspaceName             string
	Namespace                 string
	WorkloadKind              string
	WorkloadName              string
	OwnerName                 string
	PodName                   string
	PodsName                  string
	ContainerName             string
	AdapterId                 int64
	ServiceName               string
	Job                       string
	Duration                  *time.Duration
	MeterOptions              *MeterOptions
}

func NewQueryOptions() *QueryOptions {
	return &QueryOptions{}
}

type AdapterOption struct {
	AdapterId    int64
	ClustersName string
}

func (a AdapterOption) Apply(o *QueryOptions) {
	o.Level = LevelAdapter
	o.AdapterId = a.AdapterId
	o.ClustersName = a.ClustersName
}

type ClusterOption struct {
	AdapterId   int64
	ClusterName string
}

func (c ClusterOption) Apply(o *QueryOptions) {
	o.Level = LevelCluster
	o.AdapterId = c.AdapterId
	o.ClusterName = c.ClusterName
}

type NodeOption struct {
	ResourceFilter   string
	NodeName         string
	PVCFilter        string
	StorageClassName string
	QueryType        string
}

func (no NodeOption) Apply(o *QueryOptions) {
	o.Level = LevelNode
	o.ResourceFilter = no.ResourceFilter
	o.NodeName = no.NodeName
	o.QueryType = no.QueryType
}

type WorkspaceOption struct {
	ResourceFilter   string
	WorkspaceName    string
	PVCFilter        string
	StorageClassName string
}

func (wo WorkspaceOption) Apply(o *QueryOptions) {
	o.Level = LevelWorkspace
	o.ResourceFilter = wo.ResourceFilter
	o.WorkspaceName = wo.WorkspaceName
}

type NamespaceOption struct {
	ResourceFilter   string
	WorkspaceName    string
	NamespaceName    string
	PVCFilter        string
	StorageClassName string
}

func (no NamespaceOption) Apply(o *QueryOptions) {
	o.Level = LevelNamespace
	o.ResourceFilter = no.ResourceFilter
	o.WorkspaceName = no.WorkspaceName
	o.Namespace = no.NamespaceName
}

// ApplicationsOption & OpenpitrixsOption share the same ApplicationOption struct
type ApplicationOption struct {
	NamespaceName         string
	Application           string
	ApplicationComponents []string
	StorageClassName      string
}

func (ao ApplicationOption) Apply(o *QueryOptions) {
	o.Level = LevelApplication
	o.Namespace = ao.NamespaceName

	app_components := strings.Join(ao.ApplicationComponents[:], "|")

	if len(app_components) > 0 {
		o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.Namespace, app_components)
	} else {
		o.ResourceFilter = fmt.Sprintf(`namespace="%s", workload=~"%s"`, o.Namespace, ".*")
	}
}

type WorkloadOption struct {
	ResourceFilter string
	NamespaceName  string
	WorkloadKind   string
}

func (wo WorkloadOption) Apply(o *QueryOptions) {
	o.Level = LevelController
	o.ResourceFilter = wo.ResourceFilter
	o.Namespace = wo.NamespaceName
	o.WorkloadKind = wo.WorkloadKind
}

type ServiceOption struct {
	ResourceFilter string
	NamespaceName  string
	ServiceName    string
	PodNames       []string
}

type PodOption struct {
	NamespacedResourcesFilter string
	ResourceFilter            string
	NodeName                  string
	NamespaceName             string
	WorkloadKind              string
	WorkloadName              string
	PodName                   string
}

type ControllerOption struct {
	Namespace    string
	Kind         string
	WorkloadName string
	PodsName     string
	Level        string
}

func (po PodOption) Apply(o *QueryOptions) {
	o.Level = LevelPod
	o.NamespacedResourcesFilter = po.NamespacedResourcesFilter
	o.ResourceFilter = po.ResourceFilter
	o.NodeName = po.NodeName
	o.Namespace = po.NamespaceName
	o.WorkloadKind = po.WorkloadKind
	o.OwnerName = po.WorkloadName
	o.PodName = po.PodName
}

func (co ControllerOption) Apply(o *QueryOptions) {
	o.Level = LevelController
	o.Namespace = co.Namespace
	o.WorkloadKind = co.Kind
	o.WorkloadName = co.WorkloadName

}

type ContainerOption struct {
	ResourceFilter string
	NamespaceName  string
	PodName        string
	ContainerName  string
}

func (co ContainerOption) Apply(o *QueryOptions) {
	o.Level = LevelContainer
	o.ResourceFilter = co.ResourceFilter
	o.Namespace = co.NamespaceName
	o.PodName = co.PodName
	o.ContainerName = co.ContainerName
}

type PVCOption struct {
	ResourceFilter            string
	NamespaceName             string
	StorageClassName          string
	PersistentVolumeClaimName string
}

func (po PVCOption) Apply(o *QueryOptions) {
	o.Level = LevelPVC
	o.ResourceFilter = po.ResourceFilter
	o.Namespace = po.NamespaceName
}

type ComponentOption struct{}

func (_ ComponentOption) Apply(o *QueryOptions) {
	o.Level = LevelComponent
}

type MeterOption struct {
	Start time.Time
	End   time.Time
	Step  time.Duration
}

func (mo MeterOption) Apply(o *QueryOptions) {
	o.MeterOptions = &MeterOptions{
		Start: mo.Start,
		End:   mo.End,
		Step:  mo.Step,
	}
}
