package pubsubclient

import "encoding/json"

type msgType string

const (
	// Messages sent by the client
	listen   msgType = "LISTEN"
	unlisten msgType = "UNLISTEN"
	ping     msgType = "PING"

	// Messages sent by the server
	message   msgType = "MESSAGE"
	pong      msgType = "PONG"
	reconnect msgType = "RECONNECT"
	response  msgType = "RESPONSE"
)

type typedMessage interface {
	msgType() msgType
}

type msg struct {
	Type msgType `json:"type"`
}

func (m *msg) msgType() msgType {
	return m.Type
}

func unmarshal(raw []byte) (typedMessage, error) {
	base := new(msg)
	err := json.Unmarshal(raw, base)
	if err != nil {
		return nil, err
	}

	var m typedMessage
	switch base.Type {
	case listen:
		m = new(listenMsg)
	case unlisten:
		m = new(unlistenMsg)
	case ping:
		m = new(pingMsg)
	case pong:
		m = new(pongMsg)
	case reconnect:
		m = new(reconnectMsg)
	case message:
		m = new(messageMsg)
	case response:
		m = new(responseMsg)
	}

	err = json.Unmarshal(raw, m)
	if err != nil {
		return nil, err
	}
	return m, nil
}

type messageMsg struct {
	msg
	Data Message
}

type listenMsg struct {
	msg
	Nonce string    `json:"nonce,omitempty"`
	Data  topicList `json:"data"`
}

type topicList struct {
	Topics    []string `json:"topics"`
	AuthToken string   `json:"auth_token,omitempty"`
}

type unlistenMsg struct {
	msg
	Nonce string    `json:"nonce,omitempty"`
	Data  topicList `json:"data"`
}

type responseMsg struct {
	msg
	Nonce string `json:"nonce,omitempty"`
	Error string `json:"error,omitempty"`
}

type pongMsg struct {
	msg
}

type pingMsg struct {
	msg
}

type reconnectMsg struct {
	msg
}
