package util

import (
	"encoding/json"
	"net/http"

	"github.com/juju/errors"
	"github.com/pingcap/schrodinger/pkg/test_client"
	"github.com/pingcap/schrodinger/util"
)

const (
	// StateUp is state up for tikv
	StateUp = "Up"
	// StateHealth is state health up for pd
	StateHealth = true
)

type store struct {
	Count  int      `json:"count"`
	Stores []*Store `json:"stores"`
}

// Store is store for tikv health
type Store struct {
	State State `json:"store"`
}

// State is state struct for store
type State struct {
	// StateName is state name
	StateName string `json:"state_name"`
}

// PdHealth is pd health struct
type PdHealth struct {
	Pds []*PD
}

// PD is pd struct for http health api
type PD struct {
	Health bool `json:"health"`
}

// Health is Health for tikv/pd
type Health struct {
	Count int
	Alive int
}

// path /pd/api/v1/stores for store
// path /health for health
func getRunStatus(pdAddrs []string, url string, s interface{}) error {
	for _, addr := range pdAddrs {
		resp, err := util.DoGet(http.DefaultClient, util.GenURL(addr+url))
		if err != nil {
			client.Errorf("[nemesis] get PD %s store failed: %v", addr, err)
			continue
		}
		err = json.Unmarshal(resp, s)
		if err != nil {
			client.Errorf("[nemesis] unmarshal store value error: %v", err)
			continue
		}
		return nil
	}
	return errors.New("Can not find available pd")
}

// GetPdStatus gets pd status from pd
func GetPdStatus(pdAddrs []string) *Health {
	var pds []*PD
	h := &Health{}

	err := getRunStatus(pdAddrs, "/pd/health", &pds)
	if err != nil {
		return h
	}

	for _, pd := range pds {
		if pd.Health == StateHealth {
			h.Alive++
		}
		h.Count++
	}
	return h
}

// GetTikvStatus gets tikv status from pd
func GetTikvStatus(pdAddrs []string) *Health {
	h := &Health{}
	var s store
	err := getRunStatus(pdAddrs, "/pd/api/v1/stores", &s)
	if err != nil {
		return h
	}

	for _, tikv := range s.Stores {
		if tikv.State.StateName == StateUp {
			h.Alive++
		}
		h.Count++
	}
	return h
}
