package prom_config

import (
	"fmt"
	"net/url"

	"github.com/tidbcloud/infra-api/pkg/o11y/prom_config/promauth"
	"github.com/tidbcloud/infra-api/pkg/o11y/prom_config/promrelabel"
	"github.com/tidbcloud/infra-api/pkg/o11y/prom_config/promutils"
)

// PromConfig represents essential parts from Prometheus config defined at https://prometheus.io/docs/prometheus/latest/configuration/configuration/
type PromConfig struct {
	Global            GlobalConfig    `yaml:"global,omitempty"`
	ScrapeConfigs     []*ScrapeConfig `yaml:"scrape_configs,omitempty"`
	ScrapeConfigFiles []string        `yaml:"scrape_config_files,omitempty"`
}

// GlobalConfig represents essential parts for `global` section of Prometheus config.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/
type GlobalConfig struct {
	ScrapeInterval *promutils.Duration `yaml:"scrape_interval,omitempty"`
	ScrapeTimeout  *promutils.Duration `yaml:"scrape_timeout,omitempty"`
	ExternalLabels map[string]string   `yaml:"external_labels,omitempty"`
}

// ScrapeConfig represents essential parts for `scrape_config` section of Prometheus config.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
type ScrapeConfig struct {
	JobName              string                      `yaml:"job_name"`
	ScrapeInterval       *promutils.Duration         `yaml:"scrape_interval,omitempty"`
	ScrapeTimeout        *promutils.Duration         `yaml:"scrape_timeout,omitempty"`
	MetricsPath          string                      `yaml:"metrics_path,omitempty"`
	HonorLabels          bool                        `yaml:"honor_labels,omitempty"`
	HonorTimestamps      *bool                       `yaml:"honor_timestamps,omitempty"`
	FollowRedirects      *bool                       `yaml:"follow_redirects,omitempty"`
	Scheme               string                      `yaml:"scheme,omitempty"`
	Params               map[string][]string         `yaml:"params,omitempty"`
	HTTPClientConfig     promauth.HTTPClientConfig   `yaml:",inline"`
	ProxyURL             *URL                        `yaml:"proxy_url,omitempty"`
	RelabelConfigs       []promrelabel.RelabelConfig `yaml:"relabel_configs,omitempty"`
	MetricRelabelConfigs []promrelabel.RelabelConfig `yaml:"metric_relabel_configs,omitempty"`
	SampleLimit          int                         `yaml:"sample_limit,omitempty"`

	FileSDConfigs       []FileSDConfig       `yaml:"file_sd_configs,omitempty"`
	HTTPSDConfigs       []HTTPSDConfig       `yaml:"http_sd_configs,omitempty"`
	KubernetesSDConfigs []KubernetesSDConfig `yaml:"kubernetes_sd_configs,omitempty"`
	StaticConfigs       []StaticConfig       `yaml:"static_configs,omitempty"`

	// These options are supported only by lib/promscrape.
	RelabelDebug        bool                       `yaml:"relabel_debug,omitempty"`
	MetricRelabelDebug  bool                       `yaml:"metric_relabel_debug,omitempty"`
	DisableCompression  bool                       `yaml:"disable_compression,omitempty"`
	DisableKeepAlive    bool                       `yaml:"disable_keepalive,omitempty"`
	StreamParse         bool                       `yaml:"stream_parse,omitempty"`
	ScrapeAlignInterval *promutils.Duration        `yaml:"scrape_align_interval,omitempty"`
	ScrapeOffset        *promutils.Duration        `yaml:"scrape_offset,omitempty"`
	SeriesLimit         int                        `yaml:"series_limit,omitempty"`
	ProxyClientConfig   promauth.ProxyClientConfig `yaml:",inline"`
}

// FileSDConfig represents file-based service discovery config.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#file_sd_config
type FileSDConfig struct {
	Files []string `yaml:"files"`
	// `refresh_interval` is ignored. See `-promscrape.fileSDCheckInterval`
}

// StaticConfig represents essential parts for `static_config` section of Prometheus config.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#static_config
type StaticConfig struct {
	Targets []string          `yaml:"targets"`
	Labels  map[string]string `yaml:"labels,omitempty"`
}

// URL implements YAML.Marshaler and yaml.Unmarshaler interfaces for url.URL.
type URL struct {
	URL *url.URL
}

// MarshalYAML implements yaml.Marshaler interface.
func (u *URL) MarshalYAML() (interface{}, error) {
	if u.URL == nil {
		return nil, nil
	}
	return u.URL.String(), nil
}

// UnmarshalYAML implements yaml.Unmarshaler interface.
func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	parsedURL, err := url.Parse(s)
	if err != nil {
		return fmt.Errorf("cannot parse proxy_url=%q as *url.URL: %w", s, err)
	}
	u.URL = parsedURL
	return nil
}

type HTTPSDConfig struct {
	URL               string                     `yaml:"url"`
	HTTPClientConfig  promauth.HTTPClientConfig  `yaml:",inline"`
	ProxyURL          *URL                       `yaml:"proxy_url,omitempty"`
	ProxyClientConfig promauth.ProxyClientConfig `yaml:",inline"`
}

type KubernetesSDConfig struct {
	APIServer string `yaml:"api_server,omitempty"`

	// Use role() function for accessing the Role field
	Role string `yaml:"role"`
	// The filepath to kube config.
	// If defined any cluster connection information from HTTPClientConfig is ignored.
	KubeConfigFile string `yaml:"kubeconfig_file"`

	HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"`
	ProxyURL         *URL                      `yaml:"proxy_url,omitempty"`
	Namespaces       Namespaces                `yaml:"namespaces,omitempty"`
	Selectors        []Selector                `yaml:"selectors,omitempty"`
	AttachMetadata   AttachMetadata            `yaml:"attach_metadata,omitempty"`
}

// Namespaces represents namespaces for SDConfig
type Namespaces struct {
	OwnNamespace bool     `yaml:"own_namespace"`
	Names        []string `yaml:"names"`
}

// Selector represents kubernetes selector.
//
// See https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
// and https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
type Selector struct {
	Role  string `yaml:"role"`
	Label string `yaml:"label"`
}

// AttachMetadata represents `attach_metadata` option at `kuberentes_sd_config`.
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config
type AttachMetadata struct {
	Node bool `yaml:"node"`
}
