package profiling

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"

	pd "github.com/pingcap/endless/pkg/client/pd"
	tikv "github.com/pingcap/endless/pkg/client/tikv"
	"github.com/pingcap/endless/testcase/oltp/oltppkg"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	"go.uber.org/zap"
)

func AllTiKVProfiling(tc resource.TiDBCluster, profilingType string, interval string) error {
	var path string
	var err error
	// such as http://tc-tikv-0.tc-tikv-peer.test.svc:20180/debug/pprof/profile?interval=5
	if profilingType == "profile" {
		path = fmt.Sprintf("/debug/pprof/%s?interval=%s", profilingType, interval)
	} else {
		err = errors.New("Not Applicable")
		return err
	}
	tikvStatusAddrs, err := tc.PeerURLs(resource.KVStatus)
	if err != nil {
		return err
	}
	for _, tikvStatusAddr := range tikvStatusAddrs {
		log.Info("info", zap.String("tikvStatus", tikvStatusAddr.String()), zap.String("path", path))
		tikvClient, err := tikv.NewTiKVClient(tikvStatusAddr)
		if err != nil {
			return err
		}
		resp, err := tikvClient.Get(path)
		if err != nil {
			return nil
		}
		defer resp.Body.Close()
		log.Info(fmt.Sprintf("StatusCode %v", resp.StatusCode))
		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("GET request \"%s\", got %v", path, resp.StatusCode)
		}
	}
	return nil
}

func PDProfiling(tc resource.TiDBCluster, profilingType string, interval string) error {
	var path string
	var err error
	// such as http://tc-pd-0.tc-pd-peer.test.svc:2379/pd/api/v1/debug/pprof/profile?interval=30
	// if type is zip, will get all types of profiling
	if profilingType == "profile" || profilingType == "heap" || profilingType == "goroutine" || profilingType == "mutex" || profilingType == "zip" {
		path = fmt.Sprintf("/pd/api/v1/debug/pprof/%s?interval=%s", profilingType, interval)
	} else {
		err = errors.New("Not Applicable")
		return err
	}

	pdAddrs, err := tc.PeerURLs(resource.PDAddr)
	if err != nil {
		return err
	}
	for _, pdAddr := range pdAddrs {
		log.Info("info", zap.String("pdAddr", pdAddr.String()), zap.String("path", path))
		pdClient, err := pd.NewPDClientVarUrl(pdAddr)
		if err != nil {
			return err
		}
		resp, err := pdClient.Get(path)
		if err != nil {
			return nil
		}
		defer resp.Body.Close()
		log.Info(fmt.Sprintf("StatusCode %v", resp.StatusCode))
		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("GET request \"%s\", got %v", path, resp.StatusCode)
		}
	}
	return nil
}

// ActivateAllTiKVHeapProfile is used for generating all tikv heap profile automatically.
func ActivateAllTiKVHeapProfiling(ctx context.Context, tc resource.TiDBCluster, interval string) error {
	tikvVer := oltppkg.GetCompVersion(ctx, tc, "tikv")
	curVerShort := strings.TrimSuffix(strings.TrimPrefix(tikvVer, "v"), "-pre")
	if oltppkg.CompareVersion(curVerShort, "5.3.0") < 0 {
		log.Info("do not support activate tikv heap profiling if version is less than 5.3.0")
		return nil
	}
	tikvStatusAddrs, err := tc.PeerURLs(resource.KVStatus)
	if err != nil {
		return err
	}
	path := "/debug/pprof/heap_activate?" + interval
	for _, tikvStatusAddr := range tikvStatusAddrs {
		tikvClient, err := tikv.NewTiKVClient(tikvStatusAddr)
		if err != nil {
			return err
		}
		b, err := tikvClient.GetJsonBytes(path)
		if err != nil {
			return err
		}
		log.Info("info", zap.String("tikvStatus", tikvStatusAddr.String()), zap.String("activateResult", string(b)))
	}
	return nil
}
