package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"

	"github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/kvproto/pkg/tikvpb"
	"github.com/pingcap/schrodinger-test/util"
	"google.golang.org/grpc"
)

var (
	conns []*grpc.ClientConn

	pdAddr           = ""
	schedulersPrefix = "pd/api/v1/schedulers"
)

func InitClient(PdAddr string, TiKVAddr []string) error {
	pdAddr = "//" + PdAddr
	for _, addr := range TiKVAddr {
		conn, err := grpc.Dial(addr, grpc.WithInsecure())
		if err != nil {
			return err
		}
		conns = append(conns, conn)
	}

	return nil
}

// StartShuffleRegions StartShuffleRegions
func StartShuffleRegions() error {
	input := make(map[string]interface{})
	input["name"] = "shuffle-region-scheduler"
	return postJSON(schedulersPrefix, input)
}

func Close() {
	for _, conn := range conns {
		conn.Close()
	}
}

func DestroyRange(ctx context.Context, startKey []byte, endKey []byte) error {
	req := &kvrpcpb.UnsafeDestroyRangeRequest{
		StartKey: startKey,
		EndKey:   endKey,
	}

	var wg sync.WaitGroup
	for _, conn := range conns {
		client := tikvpb.NewTikvClient(conn)
		wg.Add(1)
		go func() {
			defer wg.Done()
			// delayed with random time
			time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
			_, err := client.UnsafeDestroyRange(ctx, req)
			if err != nil {
				log.Fatal("unsafe destroy range failed: %v", err)
			}
		}()
	}
	wg.Wait()

	return nil
}

func postJSON(prefix string, input map[string]interface{}) error {
	data, err := json.Marshal(input)
	if err != nil {
		return err
	}
	url := getAddress(prefix)

	var resp *http.Response
	err = util.RunWithRetry(context.Background(), 5, 3*time.Second, func() (err error) {
		resp, err = http.Post(url, "application/json", bytes.NewBuffer(data))
		return err
	})
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("statuscode is not okay: %s", getResponseError(resp))
	}

	return nil
}

func getResponseError(r *http.Response) string {
	buf := new(bytes.Buffer)
	buf.ReadFrom(r.Body)
	return fmt.Sprintf("[%d] %s", r.StatusCode, buf.String())
}

func getAddress(prefix string) string {
	u, err := url.Parse(pdAddr)
	if err != nil {
		return err.Error()
	}
	if u.Scheme == "" {
		u.Scheme = "http"
	}
	s := fmt.Sprintf("%s/%s", u, prefix)
	return s
}
