package promrelabel

import (
	"fmt"
	"strconv"
	"strings"
)

// RelabelConfig represents relabel config.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
type RelabelConfig struct {
	SourceLabels []string        `yaml:"source_labels,flow,omitempty"`
	Separator    *string         `yaml:"separator,omitempty"`
	TargetLabel  string          `yaml:"target_label,omitempty"`
	Regex        *MultiLineRegex `yaml:"regex,omitempty"`
	Modulus      uint64          `yaml:"modulus,omitempty"`
	Replacement  *string         `yaml:"replacement,omitempty"`
	Action       string          `yaml:"action,omitempty"`
	If           *IfExpression   `yaml:"if,omitempty"`

	// Match is used together with Labels for `action: graphite`. For example:
	// - action: graphite
	//   match: 'foo.*.*.bar'
	//   labels:
	//     job: '$1'
	//     instance: '${2}:8080'
	Match string `yaml:"match,omitempty"`

	// Labels is used together with Match for `action: graphite`. For example:
	// - action: graphite
	//   match: 'foo.*.*.bar'
	//   labels:
	//     job: '$1'
	//     instance: '${2}:8080'
	Labels map[string]string `yaml:"labels,omitempty"`
}

// MultiLineRegex contains a regex, which can be split into multiple lines.
//
// These lines are joined with "|" then.
// For example:
//
// regex:
// - foo
// - bar
//
// is equivalent to:
//
// regex: "foo|bar"
type MultiLineRegex struct {
	S string
}

// UnmarshalYAML unmarshals mlr from YAML passed to f.
func (mlr *MultiLineRegex) UnmarshalYAML(f func(interface{}) error) error {
	var v interface{}
	if err := f(&v); err != nil {
		return fmt.Errorf("cannot parse multiline regex: %w", err)
	}
	s, err := stringValue(v)
	if err != nil {
		return err
	}
	mlr.S = s
	return nil
}

func stringValue(v interface{}) (string, error) {
	if v == nil {
		return "null", nil
	}
	switch x := v.(type) {
	case []interface{}:
		a := make([]string, len(x))
		for i, xx := range x {
			s, err := stringValue(xx)
			if err != nil {
				return "", err
			}
			a[i] = s
		}
		return strings.Join(a, "|"), nil
	case string:
		return x, nil
	case float64:
		return strconv.FormatFloat(x, 'f', -1, 64), nil
	case int:
		return strconv.Itoa(x), nil
	case bool:
		if x {
			return "true", nil
		}
		return "false", nil
	default:
		return "", fmt.Errorf("unexpected type for `regex`: %T; want string or []string", v)
	}
}

// MarshalYAML marshals mlr to YAML.
func (mlr *MultiLineRegex) MarshalYAML() (interface{}, error) {
	a := strings.Split(mlr.S, "|")
	if len(a) == 1 {
		return a[0], nil
	}
	return a, nil
}
