package blockstore

import (
	"bytes"
	"context"

	"github.com/gorilla/websocket"
	"github.com/libp2p/go-msgio"
	"golang.org/x/xerrors"
)

type wsWrapper struct {
	wc *websocket.Conn

	nextMsg []byte
}

func (w *wsWrapper) Read(b []byte) (int, error) {
	return 0, xerrors.New("read unsupported")
}

func (w *wsWrapper) ReadMsg() ([]byte, error) {
	if w.nextMsg != nil {
		nm := w.nextMsg
		w.nextMsg = nil
		return nm, nil
	}

	mt, r, err := w.wc.NextReader()
	if err != nil {
		return nil, err
	}

	switch mt {
	case websocket.BinaryMessage, websocket.TextMessage:
	default:
		return nil, xerrors.Errorf("unexpected message type")
	}

	// todo pool
	// todo limit sizes
	var mbuf bytes.Buffer
	if _, err := mbuf.ReadFrom(r); err != nil {
		return nil, err
	}

	return mbuf.Bytes(), nil
}

func (w *wsWrapper) ReleaseMsg(bytes []byte) {
	// todo use a pool
}

func (w *wsWrapper) NextMsgLen() (int, error) {
	if w.nextMsg != nil {
		return len(w.nextMsg), nil
	}

	mt, msg, err := w.wc.ReadMessage()
	if err != nil {
		return 0, err
	}

	switch mt {
	case websocket.BinaryMessage, websocket.TextMessage:
	default:
		return 0, xerrors.Errorf("unexpected message type")
	}

	w.nextMsg = msg
	return len(w.nextMsg), nil
}

func (w *wsWrapper) Write(bytes []byte) (int, error) {
	return 0, xerrors.New("write unsupported")
}

func (w *wsWrapper) WriteMsg(bytes []byte) error {
	return w.wc.WriteMessage(websocket.BinaryMessage, bytes)
}

func (w *wsWrapper) Close() error {
	return w.wc.Close()
}

var _ msgio.ReadWriteCloser = &wsWrapper{}

func wsConnToMio(wc *websocket.Conn) msgio.ReadWriteCloser {
	return &wsWrapper{
		wc: wc,
	}
}

func HandleNetBstoreWS(ctx context.Context, bs Blockstore, wc *websocket.Conn) *NetworkStoreHandler {
	return HandleNetBstoreStream(ctx, bs, wsConnToMio(wc))
}

func NewNetworkStoreWS(wc *websocket.Conn) *NetworkStore {
	return NewNetworkStore(wsConnToMio(wc))
}