package client

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"sync"
)

// ErrStreamOverflow is returned by streamBuffer.Read once the buffer has
// exceeded its limit. The tunnel is unusable at that point and must be torn
// down; the alternative would be growing without bound.
var ErrStreamOverflow = errors.New("tunnel buffer overflow")

// defaultStreamLimit caps how much unread tunnel data we hold.
//
// USB/IP traffic is request/response, so the consumer normally keeps up. A
// backlog this large means the USB side has stalled, and 8 MB is far more
// than any legitimate burst of in-flight URBs.
const defaultStreamLimit = 8 << 20

// streamBuffer is an unbounded-write, blocking-read byte pipe.
//
// It replaces io.Pipe on the path from the WebSocket read loop into the
// USB/IP server. io.Pipe is synchronous: a Write blocks until a Reader has
// consumed the bytes, so feeding it from the WebSocket read loop meant one
// slow USB transfer froze the entire client — no control messages, no
// keepalives, no other tunnel. Writes here never block.
type streamBuffer struct {
	mu     sync.Mutex
	cond   *sync.Cond
	buf    bytes.Buffer
	limit  int
	closed bool
	err    error
}

func newStreamBuffer() *streamBuffer {
	return newStreamBufferLimit(defaultStreamLimit)
}

func newStreamBufferLimit(limit int) *streamBuffer {
	s := &streamBuffer{limit: limit}
	s.cond = sync.NewCond(&s.mu)
	return s
}

// Write appends data to the buffer and never blocks.
// Once the limit is exceeded the stream is failed: further reads drain what
// is already buffered and then return ErrStreamOverflow.
func (s *streamBuffer) Write(p []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.closed {
		return 0, io.ErrClosedPipe
	}
	if s.err != nil {
		return 0, s.err
	}

	if s.buf.Len()+len(p) > s.limit {
		s.err = fmt.Errorf("%w: %d bytes buffered, limit %d", ErrStreamOverflow, s.buf.Len(), s.limit)
		s.cond.Broadcast()
		return 0, s.err
	}

	n, err := s.buf.Write(p)
	s.cond.Broadcast()
	return n, err
}

// Read blocks until data is available, the stream is closed, or it failed.
func (s *streamBuffer) Read(p []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	for s.buf.Len() == 0 {
		if s.err != nil {
			return 0, s.err
		}
		if s.closed {
			return 0, io.EOF
		}
		s.cond.Wait()
	}

	return s.buf.Read(p)
}

// Close makes pending and future reads return EOF once the buffer is drained.
func (s *streamBuffer) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.closed = true
	s.cond.Broadcast()
	return nil
}

// Buffered reports how many bytes are waiting to be read.
func (s *streamBuffer) Buffered() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.buf.Len()
}
