package cql

import (
	"io"
	"net"
	"testing"
	"time"

	"go.uber.org/zap"
)

func TestConnStateNotBlockedByWriteLock(t *testing.T) {
	client, serverConn := net.Pipe()
	defer client.Close()
	defer serverConn.Close()

	srv := &Server{logger: zap.NewNop(), options: &ServerOptions{}}
	conn := NewConn(client, srv)

	conn.writeMu.Lock()
	done := make(chan struct{})
	go func() {
		conn.SetKeyspace("ks")
		close(done)
	}()

	select {
	case <-done:
	case <-time.After(100 * time.Millisecond):
		t.Fatal("SetKeyspace blocked while writeMu held")
	}

	conn.writeMu.Unlock()
}

func TestConnWriteFrameWaitsForWriteLock(t *testing.T) {
	client, serverConn := net.Pipe()
	defer client.Close()
	defer serverConn.Close()

	go func() {
		_, _ = io.Copy(io.Discard, serverConn)
	}()

	srv := &Server{logger: zap.NewNop(), options: &ServerOptions{}}
	conn := NewConn(client, srv)

	frame := &Frame{
		Version: ResponseVersion(ProtoVersion4),
		Stream:  1,
		Opcode:  OpcodeReady,
	}

	conn.writeMu.Lock()
	done := make(chan error, 1)
	go func() {
		done <- conn.writeFrame(frame, nil)
	}()

	select {
	case err := <-done:
		t.Fatalf("writeFrame returned early: %v", err)
	case <-time.After(50 * time.Millisecond):
	}

	conn.writeMu.Unlock()

	select {
	case err := <-done:
		if err != nil {
			t.Fatalf("writeFrame failed after unlock: %v", err)
		}
	case <-time.After(200 * time.Millisecond):
		t.Fatal("writeFrame did not complete after unlock")
	}
}
