package tsdb

import (
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/config"
	"github.com/pingcap-inc/clinic/pkg/db"
)

const (
	forceMergePath = "/internal/force_merge"
)

// cant add clean time duration
func DeleteByCluster(clusterId string, orgID uint, startTime, endTime time.Time, mc config.MetricConfig) error {
	apiUrl := fmt.Sprintf("http://%s:%d", mc.SelectAddr.OpAddress.Host, mc.SelectAddr.OpAddress.Port)
	resource := fmt.Sprintf("/delete/%d/prometheus/api/v1/admin/tsdb/delete_series", orgID)
	data := url.Values{}
	data.Set("match[]", fmt.Sprintf("{tidb_cluster=\"%s\"}", clusterId))

	u, _ := url.ParseRequestURI(apiUrl)
	u.Path = resource
	urlStr := u.String()

	client := &http.Client{}
	req, err := http.NewRequest(http.MethodPost, urlStr, strings.NewReader(data.Encode())) // URL-encoded payload
	if err != nil {
		log.Error("request for delete metrics data failed", zap.Error(err))
		return err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Error("http request failed, ", zap.Error(err))
		return err
	}
	defer func() {
		_, _ = io.Copy(io.Discard, resp.Body)
		_ = resp.Body.Close()
	}()

	if resp.StatusCode != http.StatusNoContent {
		return fmt.Errorf("not succes, %s", resp.Body)
	}
	return nil
}

func CleanVm(files []db.UploadFile, orgID uint, mc config.MetricConfig) error {
	var err error
	for _, file := range files {
		if file.BeginTime == nil || file.EndTime == nil {
			continue
		}
		err = DeleteByCluster(file.ClusterID, orgID, *file.BeginTime, *file.EndTime, mc)
	}
	return err
}

func ForceMerge(partitionPrefix string) error {
	_, records, err := net.LookupSRV("http", "tcp", "clinic-victoria-metrics-cluster-vmstorage")
	if err != nil {
		log.Error("lookup srv failed", zap.Error(err))
		return err
	}
	log.Info("force merge on vmstorage pods", zap.Any("srvrecords", records))
	// since the workload is not heavy can use native http client.
	var rpcErr error
	sendRequestFn := func(authority, pathWithQuery string) error {
		u := fmt.Sprintf("http://%s%s", authority, pathWithQuery)
		resp, err := http.Get(u)
		if err != nil {
			log.Error("http get request failed", zap.Error(err))
			return err
		}
		defer func() {
			_, _ = io.Copy(io.Discard, resp.Body)
			_ = resp.Body.Close()
		}()
		if resp.StatusCode > 300 {
			log.Error("response status abnormal", zap.Int("status", resp.StatusCode))
			return errors.New(resp.Status)
		}
		return nil
	}
	p := forceMergePath
	if len(partitionPrefix) > 0 {
		p = fmt.Sprintf("%s?partition_prefix=%s", p, partitionPrefix)
	}

	for _, record := range records {
		authority := fmt.Sprintf("%s:%d", record.Target, record.Port)
		if err := sendRequestFn(authority, p); err != nil {
			rpcErr = err
			log.Error("send request failed", zap.String("authority", authority), zap.String("path", p))
		}
	}
	return rpcErr
}
