// This file is automatically generated by scripts/generate_metric_enums.py
// Do not edit this file manually, your changes will be overwritten

#include "duckdb/main/profiling_utils.hpp"
#include "duckdb/common/enum_util.hpp"
#include "duckdb/main/profiling_node.hpp"
#include "duckdb/main/query_profiler.hpp"

#include "yyjson.hpp"

using namespace duckdb_yyjson; // NOLINT

namespace duckdb {

static string OperatorToString(const Value &val) {
    const auto type = static_cast<PhysicalOperatorType>(val.GetValue<uint8_t>());
    return EnumUtil::ToString(type);
}

template <class METRIC_TYPE>
static void AggregateMetric(ProfilingNode &node, MetricType aggregated_metric, MetricType child_metric, const std::function<METRIC_TYPE(const METRIC_TYPE &, const METRIC_TYPE &)> &update_fun) {
	auto &info = node.GetProfilingInfo();
	info.metrics[aggregated_metric] = info.metrics[child_metric];

	for (idx_t i = 0; i < node.GetChildCount(); i++) {
		auto child = node.GetChild(i);
		AggregateMetric<METRIC_TYPE>(*child, aggregated_metric, child_metric, update_fun);

		auto &child_info = child->GetProfilingInfo();
		auto value = child_info.GetMetricValue<METRIC_TYPE>(aggregated_metric);
		info.MetricUpdate<METRIC_TYPE>(aggregated_metric, value, update_fun);
	}
}

template <class METRIC_TYPE>
static void GetCumulativeMetric(ProfilingNode &node, MetricType cumulative_metric, MetricType child_metric) {
	AggregateMetric<METRIC_TYPE>(
	    node, cumulative_metric, child_metric,
	    [](const METRIC_TYPE &old_value, const METRIC_TYPE &new_value) { return old_value + new_value; });
}

static Value GetCumulativeOptimizers(ProfilingNode &node) {
	auto &metrics = node.GetProfilingInfo().metrics;
	double count = 0;
	for (auto &metric : metrics) {
		if (MetricsUtils::IsOptimizerMetric(metric.first)) {
			count += metric.second.GetValue<double>();
		}
	}
	return Value::CreateValue(count);
}

void ProfilingUtils::SetMetricToDefault(profiler_metrics_t &metrics, const MetricType &type) {
	switch(type) {
	case MetricType::ALL_OPTIMIZERS:
	case MetricType::ATTACH_LOAD_STORAGE_LATENCY:
	case MetricType::ATTACH_REPLAY_WAL_LATENCY:
	case MetricType::BLOCKED_THREAD_TIME:
	case MetricType::CHECKPOINT_LATENCY:
	case MetricType::COMMIT_LOCAL_STORAGE_LATENCY:
	case MetricType::CPU_TIME:
	case MetricType::CUMULATIVE_OPTIMIZER_TIMING:
	case MetricType::CUMULATIVE_VACUUM_TIME:
	case MetricType::LATENCY:
	case MetricType::OPERATOR_TIMING:
	case MetricType::PHYSICAL_PLANNER:
	case MetricType::PHYSICAL_PLANNER_COLUMN_BINDING:
	case MetricType::PHYSICAL_PLANNER_CREATE_PLAN:
	case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES:
	case MetricType::PLANNER:
	case MetricType::PLANNER_BINDING:
	case MetricType::WAITING_TO_ATTACH_LATENCY:
	case MetricType::WRITE_TO_WAL_LATENCY:
		metrics[type] = Value::CreateValue(0.0);
		break;
	case MetricType::CUMULATIVE_CARDINALITY:
	case MetricType::CUMULATIVE_ROWS_SCANNED:
	case MetricType::CUMULATIVE_ROW_GROUPS_SCANNED:
	case MetricType::CUMULATIVE_TOTAL_ROW_GROUPS_TO_SCAN:
	case MetricType::OPERATOR_CARDINALITY:
	case MetricType::OPERATOR_ROWS_SCANNED:
	case MetricType::OPERATOR_ROW_GROUPS_SCANNED:
	case MetricType::OPERATOR_TOTAL_ROW_GROUPS_TO_SCAN:
	case MetricType::RESULT_SET_SIZE:
	case MetricType::ROWS_RETURNED:
	case MetricType::SYSTEM_PEAK_BUFFER_MEMORY:
	case MetricType::SYSTEM_PEAK_TEMP_DIR_SIZE:
	case MetricType::TOTAL_BYTES_READ:
	case MetricType::TOTAL_BYTES_WRITTEN:
	case MetricType::TOTAL_MEMORY_ALLOCATED:
	case MetricType::WAL_REPLAY_ENTRY_COUNT:
		metrics[type] = Value::CreateValue<uint64_t>(0);
		break;
	case MetricType::EXTRA_INFO:
		metrics[type] = Value::MAP(InsertionOrderPreservingMap<string>());
		break;
	case MetricType::OPERATOR_NAME:
	case MetricType::QUERY_NAME:
		metrics[type] = Value::CreateValue("");
		break;
	case MetricType::OPERATOR_TYPE:
		metrics[type] = Value::CreateValue<uint8_t>(0);
		break;
	default:
		throw InternalException("Unknown metric type %s", EnumUtil::ToString(type));
	}
}

void ProfilingUtils::MetricToJson(duckdb_yyjson::yyjson_mut_doc *doc, duckdb_yyjson::yyjson_mut_val *dest, const char *key_ptr,  profiler_metrics_t &metrics, const MetricType &type) {
	switch(type) {
	case MetricType::ALL_OPTIMIZERS:
	case MetricType::ATTACH_LOAD_STORAGE_LATENCY:
	case MetricType::ATTACH_REPLAY_WAL_LATENCY:
	case MetricType::BLOCKED_THREAD_TIME:
	case MetricType::CHECKPOINT_LATENCY:
	case MetricType::COMMIT_LOCAL_STORAGE_LATENCY:
	case MetricType::CPU_TIME:
	case MetricType::CUMULATIVE_OPTIMIZER_TIMING:
	case MetricType::CUMULATIVE_VACUUM_TIME:
	case MetricType::LATENCY:
	case MetricType::OPERATOR_TIMING:
	case MetricType::PHYSICAL_PLANNER:
	case MetricType::PHYSICAL_PLANNER_COLUMN_BINDING:
	case MetricType::PHYSICAL_PLANNER_CREATE_PLAN:
	case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES:
	case MetricType::PLANNER:
	case MetricType::PLANNER_BINDING:
	case MetricType::WAITING_TO_ATTACH_LATENCY:
	case MetricType::WRITE_TO_WAL_LATENCY:
		yyjson_mut_obj_add_real(doc, dest, key_ptr, metrics[type].GetValue<double>());
		break;
	case MetricType::CUMULATIVE_CARDINALITY:
	case MetricType::CUMULATIVE_ROWS_SCANNED:
	case MetricType::CUMULATIVE_ROW_GROUPS_SCANNED:
	case MetricType::CUMULATIVE_TOTAL_ROW_GROUPS_TO_SCAN:
	case MetricType::OPERATOR_CARDINALITY:
	case MetricType::OPERATOR_ROWS_SCANNED:
	case MetricType::OPERATOR_ROW_GROUPS_SCANNED:
	case MetricType::OPERATOR_TOTAL_ROW_GROUPS_TO_SCAN:
	case MetricType::RESULT_SET_SIZE:
	case MetricType::ROWS_RETURNED:
	case MetricType::SYSTEM_PEAK_BUFFER_MEMORY:
	case MetricType::SYSTEM_PEAK_TEMP_DIR_SIZE:
	case MetricType::TOTAL_BYTES_READ:
	case MetricType::TOTAL_BYTES_WRITTEN:
	case MetricType::TOTAL_MEMORY_ALLOCATED:
	case MetricType::WAL_REPLAY_ENTRY_COUNT:
		yyjson_mut_obj_add_uint(doc, dest, key_ptr, metrics[type].GetValue<uint64_t>());
		break;
	case MetricType::EXTRA_INFO:
		break;
	case MetricType::OPERATOR_NAME:
	case MetricType::QUERY_NAME:
		yyjson_mut_obj_add_strcpy(doc, dest, key_ptr, metrics[type].GetValue<string>().c_str());
		break;
	case MetricType::OPERATOR_TYPE:
		yyjson_mut_obj_add_strcpy(doc, dest, key_ptr, OperatorToString(metrics[type]).c_str());
		break;
	default:
		throw InternalException("Unknown metric type %s", EnumUtil::ToString(type));
	}
}

void ProfilingUtils::CollectMetrics(const MetricType &type, QueryMetrics &query_metrics, Value &metric, ProfilingNode &node, ProfilingInfo &child_info) {
	switch(type) {
	case MetricType::CPU_TIME:
		GetCumulativeMetric<double>(node, MetricType::CPU_TIME, MetricType::OPERATOR_TIMING);
		break;
	case MetricType::CUMULATIVE_CARDINALITY:
		GetCumulativeMetric<uint64_t>(node, MetricType::CUMULATIVE_CARDINALITY, MetricType::OPERATOR_CARDINALITY);
		break;
	case MetricType::CUMULATIVE_ROWS_SCANNED:
		GetCumulativeMetric<uint64_t>(node, MetricType::CUMULATIVE_ROWS_SCANNED, MetricType::OPERATOR_ROWS_SCANNED);
		break;
	case MetricType::CUMULATIVE_ROW_GROUPS_SCANNED:
		GetCumulativeMetric<uint64_t>(node, MetricType::CUMULATIVE_ROW_GROUPS_SCANNED, MetricType::OPERATOR_ROW_GROUPS_SCANNED);
		break;
	case MetricType::CUMULATIVE_TOTAL_ROW_GROUPS_TO_SCAN:
		GetCumulativeMetric<uint64_t>(node, MetricType::CUMULATIVE_TOTAL_ROW_GROUPS_TO_SCAN, MetricType::OPERATOR_TOTAL_ROW_GROUPS_TO_SCAN);
		break;
	case MetricType::ATTACH_LOAD_STORAGE_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::ATTACH_LOAD_STORAGE_LATENCY));
		break;
	case MetricType::ATTACH_REPLAY_WAL_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::ATTACH_REPLAY_WAL_LATENCY));
		break;
	case MetricType::CHECKPOINT_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::CHECKPOINT_LATENCY));
		break;
	case MetricType::COMMIT_LOCAL_STORAGE_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::COMMIT_LOCAL_STORAGE_LATENCY));
		break;
	case MetricType::CUMULATIVE_VACUUM_TIME:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::CUMULATIVE_VACUUM_TIME));
		break;
	case MetricType::LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::LATENCY));
		break;
	case MetricType::WAITING_TO_ATTACH_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::WAITING_TO_ATTACH_LATENCY));
		break;
	case MetricType::WRITE_TO_WAL_LATENCY:
		metric = Value::DOUBLE(query_metrics.GetMetricInSeconds(MetricType::WRITE_TO_WAL_LATENCY));
		break;
	case MetricType::QUERY_NAME:
		metric = query_metrics.query_name;
		break;
	case MetricType::TOTAL_BYTES_READ:
		metric = Value::UBIGINT(query_metrics.GetMetricValue(MetricType::TOTAL_BYTES_READ));
		break;
	case MetricType::TOTAL_BYTES_WRITTEN:
		metric = Value::UBIGINT(query_metrics.GetMetricValue(MetricType::TOTAL_BYTES_WRITTEN));
		break;
	case MetricType::TOTAL_MEMORY_ALLOCATED:
		metric = Value::UBIGINT(query_metrics.GetMetricValue(MetricType::TOTAL_MEMORY_ALLOCATED));
		break;
	case MetricType::WAL_REPLAY_ENTRY_COUNT:
		metric = Value::UBIGINT(query_metrics.GetMetricValue(MetricType::WAL_REPLAY_ENTRY_COUNT));
		break;
	case MetricType::RESULT_SET_SIZE:
		metric = child_info.metrics[MetricType::RESULT_SET_SIZE];
		break;
	case MetricType::ROWS_RETURNED:
		metric = child_info.metrics[MetricType::OPERATOR_CARDINALITY];
		break;
	case MetricType::CUMULATIVE_OPTIMIZER_TIMING:
		metric = GetCumulativeOptimizers(node);
		break;
	default:
		return;
	}
}

}
