package client

import (
	"fmt"
	"net"
	"os"
	"testing"
	"time"

	"github.com/pingcap/schrodinger/agent/api"
	"github.com/pingcap/schrodinger/agent/daemon"
	"github.com/pingcap/schrodinger/buildbot/config"
	"github.com/pingcap/schrodinger/util"
	"github.com/stretchr/testify/assert"
)

const retryNum = 10

func getAgentAddr(t *testing.T) string {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("listen failed %v", err)
	}
	addr := l.Addr().String()
	l.Close()

	return addr
}

func TestAgentHandler(t *testing.T) {
	addr := getAgentAddr(t)
	node := api.NewAgent(addr)
	defer node.Close()

	client := NewClient(addr)

	go func() {
		node.Run()
	}()

	time.Sleep(time.Second)

	name := "pd"
	args := []string{
		"-peer-urls", "http://127.0.0.1:32380",
		"-client-urls", "http://127.0.0.1:32379",
	}

	latestGitHash, err := util.GetHashByBranchAndTag("master", "", "pd",
		config.GetBuildConfig().BaseConfig.DownloadAddr)
	if err != nil {
		assert.NoError(t, err)
	}

	latestBinaryURL := fmt.Sprintf("%s/download/builds/pingcap/pd/%s/centos7/pd-server.tar.gz",
		config.GetBuildConfig().BaseConfig.DownloadAddr, latestGitHash)

	defer os.RemoveAll(name)
	err = client.StartProcess(name, "pd-server", latestBinaryURL, args, 0, 0, 0)
	assert.NoError(t, err)
	waitState(t, client, name, daemon.ProcStatRunning, "start process", name)

	err = client.StartProcess(name, "pd-server", latestBinaryURL, args, 0, 0, 0)
	assert.Error(t, err)

	err = client.ShutdownProcess("pd")
	assert.NoError(t, err)
	waitState(t, client, name, daemon.ProcStatStopped, "shutdown process", name)

	err = client.RestartProcess("pd")
	assert.NoError(t, err)
	waitState(t, client, name, daemon.ProcStatRunning, "restart process", name)

	stat, err := client.ProcessDetail(name)
	assert.NoError(t, err)
	t.Logf("process state is %+v after proces restart", stat)

	err = client.StopProcess(name)
	assert.NoError(t, err)
	waitState(t, client, name, daemon.ProcStatKilled, "stop process", name)
}

func waitState(t *testing.T, client *Client, name string, expectState daemon.ProcessState, msg ...interface{}) {
	var (
		state daemon.ProcessState
		err   error
	)
	for i := 0; i < retryNum; i++ {
		state, err = client.ProcessState(name)
		assert.NoError(t, err)
		if state == expectState {
			return
		}
		time.Sleep(1 * time.Second)
	}
	assert.Equal(t, expectState, state, msg...)
}
