package vm

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/tidbcloud/observability/backup-worker/pkg/configs"
)

type Client struct {
	cli      *http.Client
	cfg      *configs.VictoriaMetrics
	endpoint string
	rp       configs.RetryPolicy
}

// New creates a VictoriaMetrics client with configured timeout.
//
// The HTTP client is configured with a single timeout value that applies
// to the entire request lifecycle including connection, transfer, and response.
func New(cfg *configs.VictoriaMetrics, rp configs.RetryPolicy) *Client {
	logrus.Infof("VictoriaMetrics client created: endpoint=%s", cfg.Endpoint)
	return &Client{
		cli: &http.Client{
			Timeout: time.Duration(cfg.Timeout),
		},
		cfg:      cfg,
		endpoint: cfg.Endpoint,
		rp:       rp,
	}
}

// Pull exports time-series data in VictoriaMetrics native format.
//
// The native format (/api/v1/export/native) is preferred over JSON because:
// - ~10x smaller payload size for the same data
// - Faster encoding/decoding on both server and client
// - Preserves full precision for floating-point values
//
// Retry behavior:
// - Retries on network errors and 5xx server errors
// - Does NOT retry on 4xx client errors (bad request, auth failure)
// - Uses configured delay interval between retries
func (c *Client) Pull(ctx context.Context, start, end int64) (io.ReadCloser, error) {
	const uri = "%s/api/v1/export/native?start=%d&end=%d&match[]=%s"
	var readCloser io.ReadCloser
	err := c.rp.Do(ctx, func() error {
		reqUrl := fmt.Sprintf(uri, c.endpoint, start, end, url.QueryEscape(c.cfg.Match))
		req, _ := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil)
		resp, err := c.cli.Do(req)
		if err != nil {
			return err
		}

		// Only retry on server errors (5xx). Client errors (4xx) indicate
		// configuration problems that won't be resolved by retrying.
		if resp.StatusCode >= 500 {
			resp.Body.Close()
			return fmt.Errorf("server error: status=%d", resp.StatusCode)
		}
		readCloser = resp.Body
		return nil
	})
	if err != nil {
		return nil, err
	}
	return readCloser, nil
}
