package metrics

import (
	"context"
	"fmt"
	"github.com/pingcap/tep/pkg/apis/apiserver"
	"github.com/pingcap/tep/pkg/metrics/provider"
	store "github.com/pingcap/tep/pkg/registry/apiserver"
	"k8s.io/apimachinery/pkg/api/errors"
	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
	"k8s.io/apimachinery/pkg/runtime"
	"strings"
)

var (
	AvgCPUUsage = "avg(1 - (avg by (instance) (irate(node_cpu_seconds_total{mode=\"idle\"}[1m]))))"
	AvgMemUsage = `
avg(( (
      node_memory_MemTotal_bytes{ job="node-exporter"}
    - node_memory_MemFree_bytes{ job="node-exporter"}
    - node_memory_Buffers_bytes{ job="node-exporter"}
    - node_memory_Cached_bytes{ job="node-exporter"}
    )
    / node_memory_MemTotal_bytes{ job="node-exporter"}
  ) )
`
	AvgNetworkIn  = "avg by (instance) (rate(node_network_receive_bytes_total{ job=\"node-exporter\"}[1m]))"
	AvgNetworkOut = "avg by (instance) (rate(node_network_transmit_bytes_total{ job=\"node-exporter\"}[1m]))"
	DiskUsage     = "avg((1- sum by (instance) (max by(device, instance) (node_filesystem_avail_bytes{device!~\"rootfs|tmpfs\"})) / sum by (instance) (max by(device, instance) (node_filesystem_size_bytes{device!~\"rootfs|tmpfs\"}))))"

	ResourceUsage = map[string]string{
		"CPU":        AvgCPUUsage,
		"MEMORY":     AvgMemUsage,
		"NETWORK_RX": AvgNetworkIn,
		"NETWORK_TX": AvgNetworkOut,
		"DISK":       DiskUsage,
	}
)

type OverviewStorage struct {
	store *store.Store
}

func NewOverviewStorage(store *store.Store) *OverviewStorage {
	return &OverviewStorage{
		store: store,
	}
}

func (s *OverviewStorage) Kind() string {
	return "OverviewMetrics"
}

func (s *OverviewStorage) New() runtime.Object {
	return &apiserver.MetricValue{}
}

func (s *OverviewStorage) NewList() runtime.Object {
	return &apiserver.MetricValueList{}
}

func (s *OverviewStorage) NamespaceScoped() bool {
	return false
}

func (s *OverviewStorage) NewExtraListOptions() runtime.Object {
	return &apiserver.OverviewMetricsListOptions{}
}

func (s *OverviewStorage) List(ctx context.Context, options *metainternalversion.ListOptions, extraOptions runtime.Object) (runtime.Object, error) {
	o := extraOptions.(*apiserver.OverviewMetricsListOptions)
	query, exist := ResourceUsage[strings.ToUpper(o.Type)]
	if !exist {
		return nil, errors.NewBadRequest("Unknown type - " + o.Type)
	}

	if o.StartTimeInSec == o.EndTimeInSec {
		r, err := s.store.K8SMetricsProvider.GetMetric(query, provider.MetricInfo{
			Metric:       "query",
			AppendLabels: map[string]string{"__name__": fmt.Sprintf("overview_%s", strings.ToLower(o.Type))},
		})
		if err != nil {
			return nil, err
		}

		return r, nil
	}
	r, err := s.store.K8SMetricsProvider.GetMetricSeries(query, o.StartTimeInSec, o.EndTimeInSec, provider.MetricInfo{
		Metric:       "query_range",
		StepInSec:    o.Step,
		AppendLabels: map[string]string{"__name__": fmt.Sprintf("overview_%s", strings.ToLower(o.Type))},
	})
	if err != nil {
		return nil, err
	}

	return r, nil
}

func (s *OverviewStorage) NeedParameter() bool {
	return true
}
