package registry

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"time"
)

const (
	DefaultDiscoveryUrl = "https://discovery.nspix.com"
)

type (
	Discovery struct {
		baseUrl string
	}

	discoveryResponse struct {
		Code    int             `json:"errno"`
		Message string          `json:"errmsg"`
		Data    json.RawMessage `json:"result"`
	}
)

func (r *Discovery) sendRequest(ctx context.Context, method, path string, body io.Reader, data interface{}) (err error) {
	var (
		req *http.Request
		res *http.Response
	)
	if req, err = http.NewRequest(method, r.baseUrl+path, body); err != nil {
		return
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	if res, err = http.DefaultClient.Do(req.WithContext(ctx)); err != nil {
		return
	}
	defer func() {
		_ = res.Body.Close()
	}()
	rv := &discoveryResponse{}
	if err = json.NewDecoder(res.Body).Decode(rv); err == nil {
		if rv.Code == 0 {
			if data != nil {
				err = json.Unmarshal(rv.Data, data)
			}
		} else {
			err = errors.New(rv.Message)
		}
	}
	return
}

func (r *Discovery) Register(instance *ServiceNode) (err error) {
	var (
		buf []byte
	)
	if buf, err = json.Marshal(instance); err != nil {
		return
	}
	ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
	return r.sendRequest(ctx, "POST", "/register", bytes.NewReader(buf), nil)
}

func (r *Discovery) Deregister(instance *ServiceNode) (err error) {
	ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
	return r.sendRequest(ctx, "DELETE", "/deregister/"+instance.ID, nil, nil)
}

func (r *Discovery) Get(name string) (instances []*ServiceNode, err error) {
	instances = make([]*ServiceNode, 0)
	ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
	err = r.sendRequest(ctx, "GET", "/service?name="+name, nil, &instances)
	return
}

func (r *Discovery) Fetch() (instances []*ServiceNode, err error) {
	instances = make([]*ServiceNode, 0)
	ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
	err = r.sendRequest(ctx, "GET", "/services", nil, &instances)
	return
}

func NewDiscovery(uri string) *Discovery {
	if uri == "" {
		uri = DefaultDiscoveryUrl
	}
	return &Discovery{
		baseUrl: uri,
	}
}
