package snapws

import (
	"net/http"
	"time"
)

type BackpressureStrategy int

const (
	// Drops the message without closing the connection.
	BackpressureDrop = iota
	// Drops and closes the connection.
	BackpressureClose
	// Blocks until the message is sent.
	//
	// Not recommended to use excpet for very very special cases.
	BackpressureWait
)

const (
	defaultWriteWait = time.Second * 5
	defaultReadWait  = time.Minute
	defaultPingEvery = time.Second * 50

	DefaultMaxMessageSize        = 1 << 20 // 1MB
	DefaultReadBufferSize        = 4096
	DefaultWriteBufferSize       = 4096
	DefaultBroadcastChannelsSize = 8
)

type Options struct {
	// Ran before finalizing and accepting the handshake.
	Middlwares []Middlware
	// Ran when the connection finalizes.
	OnConnect func(conn *Conn)
	// // Ran when connection closes.
	OnDisconnect func(conn *Conn)

	// If not set it will default to 5 seconds.
	WriteWait time.Duration
	// Should be larger than PingEvery. If not set it will default to 60 seconds.
	ReadWait time.Duration
	// If not set it will default to 50 seconds.
	PingEvery time.Duration

	// This is the max size of message sent by the client. if not set it will default to 1MB
	// -1 means there is no max size.
	// Note: the size includes the frame/frames header/header and masking key/keys.
	// PLEASE DONT USER -1 UNLESS YOU KNOW WHAT YOU ARE DOING
	MaxMessageSize int
	// maximum fragremnt allowed per an incoming message.
	// if not set it will default to 0, which means there is no limit.
	ReaderMaxFragments int
	// if not set it will use the default http buffer (4kb)
	ReadBufferSize int
	// if not set it will use the default http buffer (4kb)
	WriteBufferSize int

	// Buffer pooling can reduce GC pressure in workloads with large messages and very high throughput,
	// but may increase latency in some scenarios. Enabled by default.
	DisableWriteBuffersPooling bool

	// subProtocols defines the list of supported WebSocket sub-protocols by the server.
	// During the handshake, the server will select the first matching protocol from the
	// client's Sec-WebSocket-Protocol header, based on the client's order of preference.
	// If no match is found, the behavior depends on the value of rejectRaw.
	SubProtocols []string
	// rejectRaw determines whether to reject clients that do not propose any matching
	// sub-protocols. If set to true, the connection will be rejected when:
	//   - The client does not include any Sec-WebSocket-Protocol header.
	//   - Or none of the client's protocols match the supported subProtocols list.
	//
	// If false, such connections will be accepted as raw WebSocket connections.
	RejectRaw bool

	// Defines the size of the buffered channel per connection that receive broadcast messages.
	//
	// Default is 8.
	BroadcastChannelsSize int
	// BackpressureStrategy controls the behavior when the per-conn broadcast channel is full:
	// 	- snapws.BackpressureDrop (default): when the channel is full the message will be droped.
	// 	- snapws.BackpressureClose: when the channel is full the connection will close.
	// 	- snapws.BackpressureWait (not recommended): when the channel is full the reading loop will block
	// 	until it succeeds to send the message.
	BroadcastBackpressure BackpressureStrategy

	// SkipUTF8Validation disables UTF-8 validation for text frames.
	// if UTF8 validtion is enabled, the library would be validating text messages only when
	// ReadString() is called. Not on every read (NextReader(), Read()).
	// UTF-8 for text messages is required by RFC 6455.
	// If you need max performance and don't care about the validity of the message.
	// you can disable it.
	// Default: false (validation enabled)
	SkipUTF8Validation bool

	// If not set it will default to DefaultMaxMessageSize (1MB).
	//
	// Max size of the batched messages. For instance, you have 3 batched messages each of size 4kb,
	// the batch is of size 12kb < 1MB.
	MaxBatchSize int
}

func (opt *Options) WithDefault() {
	if opt.WriteWait == 0 {
		opt.WriteWait = defaultWriteWait
	}
	if opt.ReadWait == 0 {
		opt.ReadWait = defaultReadWait
	}
	if opt.PingEvery == 0 {
		opt.PingEvery = defaultPingEvery
	}
	if opt.MaxMessageSize == 0 {
		opt.MaxMessageSize = DefaultMaxMessageSize
	}
	if opt.WriteBufferSize == 0 {
		opt.WriteBufferSize = DefaultWriteBufferSize
	}
	if opt.BroadcastChannelsSize == 0 {
		opt.BroadcastChannelsSize = DefaultBroadcastChannelsSize
	}

	if opt.MaxBatchSize == 0 {
		opt.MaxBatchSize = DefaultMaxMessageSize
	}
}

type Middlwares []Middlware

// A function representing a middleware that will be ran after validating the websocket upgrade request
// and before switching protocols.
// If a non-nil error returns the connection wont be accepted.
// It is prefered to return an error of type snapws.MiddlewareErr.
type Middlware func(w http.ResponseWriter, r *http.Request) error

// An error that can be returned by a middleware.
// It's usefull if you want to explicitly set the HTTP response code and error message.
type MiddlewareErr struct {
	Code    int
	Message string
}

func AsMiddlewareErr(err error) (*MiddlewareErr, bool) {
	e, ok := err.(*MiddlewareErr)
	return e, ok
}

func (err *MiddlewareErr) Error() string {
	return err.Message
}

func NewMiddlewareErr(code int, message string) *MiddlewareErr {
	return &MiddlewareErr{Code: code, Message: message}
}
