// Copyright The Prometheus Authors
// 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 fuzzing

import (
	"github.com/prometheus/prometheus/promql/parser"
	"github.com/prometheus/prometheus/promql/promqltest"
)

// GetCorpusForFuzzParseMetricText returns the seed corpus for FuzzParseMetricText.
func GetCorpusForFuzzParseMetricText() [][]byte {
	return [][]byte{
		[]byte(""),
		[]byte("metric_name 1.0"),
		[]byte("# HELP metric_name help text\n# TYPE metric_name counter\nmetric_name 1.0"),
		[]byte("o { quantile = \"1.0\", a = \"b\" } 8.3835e-05"),
		[]byte("# HELP api_http_request_count The total number of HTTP requests.\n# TYPE api_http_request_count counter\nhttp_request_count{method=\"post\",code=\"200\"} 1027 1395066363000"),
		[]byte("msdos_file_access_time_ms{path=\"C:\\\\DIR\\\\FILE.TXT\",error=\"Cannot find file:\\n\\\"FILE.TXT\\\"\"} 1.234e3"),
		[]byte("metric_without_timestamp_and_labels 12.47"),
		[]byte("something_weird{problem=\"division by zero\"} +Inf -3982045"),
		[]byte("http_request_duration_seconds_bucket{le=\"+Inf\"} 144320"),
		[]byte("go_gc_duration_seconds{ quantile=\"0.9\", a=\"b\"} 8.3835e-05"),
		[]byte("go_gc_duration_seconds{ quantile=\"1.0\", a=\"b\" } 8.3835e-05"),
		[]byte("go_gc_duration_seconds{ quantile = \"1.0\", a = \"b\" } 8.3835e-05"),
	}
}

// GetCorpusForFuzzParseOpenMetric returns the seed corpus for FuzzParseOpenMetric.
func GetCorpusForFuzzParseOpenMetric() [][]byte {
	return [][]byte{
		[]byte(""),
		[]byte("# TYPE metric_name counter\nmetric_name_total 1.0"),
		[]byte("# HELP metric_name help text\n# TYPE metric_name counter\nmetric_name_total 1.0\n# EOF"),
	}
}

// GetCorpusForFuzzParseMetricSelector returns the seed corpus for FuzzParseMetricSelector.
func GetCorpusForFuzzParseMetricSelector() []string {
	return []string{
		"",
		"metric_name",
		`metric_name{label="value"}`,
		`{label="value"}`,
		`metric_name{label=~"val.*"}`,
	}
}

// GetCorpusForFuzzParseExpr returns the seed corpus for FuzzParseExpr.
func GetCorpusForFuzzParseExpr() ([]string, error) {
	// Save original values and restore them after parsing test expressions.
	defer func(funcs, durationExpr, rangeSelectors bool) {
		parser.EnableExperimentalFunctions = funcs
		parser.ExperimentalDurationExpr = durationExpr
		parser.EnableExtendedRangeSelectors = rangeSelectors
	}(parser.EnableExperimentalFunctions, parser.ExperimentalDurationExpr, parser.EnableExtendedRangeSelectors)

	// Enable experimental features to parse all test expressions.
	parser.EnableExperimentalFunctions = true
	parser.ExperimentalDurationExpr = true
	parser.EnableExtendedRangeSelectors = true

	// Get built-in test expressions.
	builtInExprs, err := promqltest.GetBuiltInExprs()
	if err != nil {
		return nil, err
	}

	// Add additional seed corpus.
	additionalExprs := []string{
		"",
		"1",
		"metric_name",
		`"str"`,
		// Numeric literals
		".5",
		"5.",
		"123.4567",
		"5e3",
		"5e-3",
		"+5.5e-3",
		"0xc",
		"0755",
		"-0755",
		"+Inf",
		"-Inf",
		// Basic binary operations
		"1 + 1",
		"1 - 1",
		"1 * 1",
		"1 / 1",
		"1 % 1",
		// Comparison operators
		"1 == 1",
		"1 != 1",
		"1 > 1",
		"1 >= 1",
		"1 < 1",
		"1 <= 1",
		// Operations with identifiers
		"foo == 1",
		"foo * bar",
		"2.5 / bar",
		"foo and bar",
		"foo or bar",
		// Complex expressions
		"+1 + -2 * 1",
		"1 + 2/(3*1)",
		// Comment
		"#comment",
	}

	return append(builtInExprs, additionalExprs...), nil
}
