package inframetrics

import (
	"strings"
	"time"

	"github.com/SigNoz/signoz/pkg/query-service/constants"
	"github.com/SigNoz/signoz/pkg/query-service/model"
)

var (
	// TODO(srikanthccv): import metadata yaml from receivers and use generated files to check the metrics
	podMetricNamesToCheck = []string{
		"k8s_pod_cpu_utilization",
		"k8s_pod_memory_usage",
		"k8s_pod_cpu_request_utilization",
		"k8s_pod_memory_request_utilization",
		"k8s_pod_cpu_limit_utilization",
		"k8s_pod_memory_limit_utilization",
		"k8s_container_restarts",
		"k8s_pod_phase",
	}
	nodeMetricNamesToCheck = []string{
		"k8s_node_cpu_utilization",
		"k8s_node_allocatable_cpu",
		"k8s_node_memory_usage",
		"k8s_node_allocatable_memory",
		"k8s_node_condition_ready",
	}
	clusterMetricNamesToCheck = []string{
		"k8s_daemonset_desired_scheduled_nodes",
		"k8s_daemonset_current_scheduled_nodes",
		"k8s_deployment_desired",
		"k8s_deployment_available",
		"k8s_job_desired_successful_pods",
		"k8s_job_active_pods",
		"k8s_job_failed_pods",
		"k8s_job_successful_pods",
		"k8s_statefulset_desired_pods",
		"k8s_statefulset_current_pods",
	}
	optionalPodMetricNamesToCheck = []string{
		"k8s_pod_cpu_request_utilization",
		"k8s_pod_memory_request_utilization",
		"k8s_pod_cpu_limit_utilization",
		"k8s_pod_memory_limit_utilization",
	}

	// did they ever send _any_ pod metrics?
	didSendPodMetricsQuery = `
	SELECT count() FROM %s.%s WHERE metric_name IN (%s)
`

	// did they ever send any node metrics?
	didSendNodeMetricsQuery = `
	SELECT count() FROM %s.%s WHERE metric_name IN (%s)
`

	// did they ever send any cluster metrics?
	didSendClusterMetricsQuery = `
	SELECT count() FROM %s.%s WHERE metric_name IN (%s)
`

	// if they ever sent _any_ pod metrics, we assume they know how to send pod metrics
	// now, are they sending optional pod metrics such request/limit metrics?
	isSendingOptionalPodMetricsQuery = `
	SELECT count() FROM %s.%s WHERE metric_name IN (%s)
`

	// there should be [cluster, node, namespace, one of (deployment, statefulset, daemonset, cronjob, job)] for each pod
	isSendingRequiredMetadataQuery = `
	SELECT any(JSONExtractString(labels, 'k8s_cluster_name')) as k8s_cluster_name,
		any(JSONExtractString(labels, 'k8s_node_name')) as k8s_node_name,
		any(JSONExtractString(labels, 'k8s_namespace_name')) as k8s_namespace_name,
		any(JSONExtractString(labels, 'k8s_deployment_name')) as k8s_deployment_name,
		any(JSONExtractString(labels, 'k8s_statefulset_name')) as k8s_statefulset_name,
		any(JSONExtractString(labels, 'k8s_daemonset_name')) as k8s_daemonset_name,
		any(JSONExtractString(labels, 'k8s_cronjob_name')) as k8s_cronjob_name,
		any(JSONExtractString(labels, 'k8s_job_name')) as k8s_job_name,
		JSONExtractString(labels, 'k8s_pod_name') as k8s_pod_name
	FROM %s.%s WHERE metric_name IN (%s)
		AND (unix_milli >= (toUnixTimestamp(now() - toIntervalMinute(60)) * 1000))
		AND JSONExtractString(labels, 'k8s_namespace_name') NOT IN ('kube-system', 'kube-public', 'kube-node-lease', 'metallb-system')
	GROUP BY k8s_pod_name
	LIMIT 1 BY k8s_cluster_name, k8s_node_name, k8s_namespace_name
`
)

// getParamsForTopItems returns the step, time series table name and samples table name
// for the top items query. what are we doing here?
// we want to identify the top hosts/pods/nodes quickly, so we use pre-aggregated data
// for samples and time series tables to speed up the query
// the speed of the query depends on the number of values in group by clause, the higher
// the step interval, the faster the query will be as number of rows to group by is reduced
// here we are using the averaged value of the time series data to get the top items
func getParamsForTopItems(start, end int64) (int64, string, string) {
	var step int64
	var timeSeriesTableName string
	var samplesTableName string

	if end-start < time.Hour.Milliseconds() {
		// 5 minute aggregation for any query less than 1 hour
		step = 5 * 60
		timeSeriesTableName = constants.SIGNOZ_TIMESERIES_v4_LOCAL_TABLENAME
		samplesTableName = constants.SIGNOZ_SAMPLES_V4_AGG_5M_TABLENAME
	} else if end-start < time.Hour.Milliseconds()*6 {
		// 15 minute aggregation for any query less than 6 hours
		step = 15 * 60
		timeSeriesTableName = constants.SIGNOZ_TIMESERIES_v4_6HRS_LOCAL_TABLENAME
		samplesTableName = constants.SIGNOZ_SAMPLES_V4_AGG_5M_TABLENAME
	} else if end-start < time.Hour.Milliseconds()*24 {
		// 1 hour aggregation for any query less than 1 day
		step = 60 * 60
		timeSeriesTableName = constants.SIGNOZ_TIMESERIES_v4_1DAY_LOCAL_TABLENAME
		samplesTableName = constants.SIGNOZ_SAMPLES_V4_AGG_30M_TABLENAME
	} else if end-start < time.Hour.Milliseconds()*7 {
		// 6 hours aggregation for any query less than 1 week
		step = 6 * 60 * 60
		timeSeriesTableName = constants.SIGNOZ_TIMESERIES_v4_1WEEK_LOCAL_TABLENAME
		samplesTableName = constants.SIGNOZ_SAMPLES_V4_AGG_30M_TABLENAME
	} else {
		// 12 hours aggregation for any query greater than 1 week
		step = 12 * 60 * 60
		timeSeriesTableName = constants.SIGNOZ_TIMESERIES_v4_1WEEK_LOCAL_TABLENAME
		samplesTableName = constants.SIGNOZ_SAMPLES_V4_AGG_30M_TABLENAME
	}
	return step, timeSeriesTableName, samplesTableName
}

func getParamsForTopHosts(req model.HostListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopProcesses(req model.ProcessListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopPods(req model.PodListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopNodes(req model.NodeListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopNamespaces(req model.NamespaceListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopClusters(req model.ClusterListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopDeployments(req model.DeploymentListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopDaemonSets(req model.DaemonSetListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopStatefulSets(req model.StatefulSetListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopJobs(req model.JobListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

func getParamsForTopVolumes(req model.VolumeListRequest) (int64, string, string) {
	return getParamsForTopItems(req.Start, req.End)
}

// TODO(srikanthccv): remove this
// What is happening here?
// The `PrepareTimeseriesFilterQuery` uses the local time series table for sub-query because each fingerprint
// goes to same shard.
// However, in this case, we are interested in the attributes values across all the shards.
// So, we replace the local time series table with the distributed time series table.
// See `PrepareTimeseriesFilterQuery` for more details.
func localQueryToDistributedQuery(query string) string {
	return strings.Replace(query, ".time_series_v4", ".distributed_time_series_v4", 1)
}
