package elasticache

import (
	"bufio"
	"bytes"
	"strconv"

	"github.com/pkg/errors"
)

type clusterConfig struct {
	version int
	nodes   []*node
}

type node struct {
	hostname string
	ip       string
	port     string
}

var (
	configCmd    = []byte("config get cluster\r\n")
	configPrefix = []byte("CONFIG")
	configEnd    = []byte("END\r\n")
)

// getConfig sends a "config get cluster" command and parses the response.
func getConfig(rw *bufio.ReadWriter) (*clusterConfig, error) {
	if _, err := rw.Write(configCmd); err != nil {
		return nil, err
	}
	if err := rw.Flush(); err != nil {
		return nil, err
	}
	cfg := new(clusterConfig)
	if err := cfg.parseResponse(rw.Reader); err != nil {
		return nil, err
	}
	return cfg, nil
}

// parseResponse parses the config/end block into the clusterConfig.
func (cfg *clusterConfig) parseResponse(r *bufio.Reader) error {
	// config
	line, err := r.ReadSlice('\n')
	if err != nil {
		return err
	}
	if !bytes.HasPrefix(line, configPrefix) {
		return errors.Errorf("expected %v got %v", configPrefix, line)
	}

	// version
	line, err = r.ReadSlice('\n')
	if err != nil {
		return err
	}
	line = bytes.Trim(line, "\r\n")
	cfg.version, err = strconv.Atoi(string(line))
	if err != nil {
		return err
	}

	// nodes
	line, err = r.ReadSlice('\n')
	if err != nil {
		return err
	}
	line = bytes.Trim(line, "\r\n")
	for _, ns := range bytes.Split(line, []byte(" ")) {
		parts := bytes.Split(ns, []byte("|"))
		if len(parts) != 3 {
			return errors.Errorf("expected 3 parts in %#v", parts)
		}
		n := &node{
			hostname: string(parts[0]),
			ip:       string(parts[1]),
			port:     string(parts[2]),
		}
		cfg.nodes = append(cfg.nodes, n)
	}

	// burn extra newline
	_, _ = r.ReadSlice('\n')

	//end
	line, err = r.ReadSlice('\n')
	if err != nil {
		return err
	}
	if !bytes.Equal(line, configEnd) {
		return errors.Errorf("expected %v got %v", configEnd, line)
	}

	return nil
}
