package gws

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/lxzan/gws/internal"
	"github.com/stretchr/testify/assert"
)

func TestNewClient(t *testing.T) {
	NewClient(new(BuiltinEventHandler), nil)
	{
		var option = &ClientOption{
			Addr: "ws://127.0.0.1",
		}
		NewClient(new(BuiltinEventHandler), option)
	}

	{
		var option = &ClientOption{
			Addr: "wss://127.0.0.1",
		}
		NewClient(new(BuiltinEventHandler), option)
	}

	{
		var option = &ClientOption{
			Addr: "unix:///",
		}
		NewClient(new(BuiltinEventHandler), option)
	}

	{
		var option = &ClientOption{
			Addr: "tls://127.0.0.1",
		}
		NewClient(new(BuiltinEventHandler), option)
	}
}

func TestNewClientFromConn(t *testing.T) {
	var as = assert.New(t)

	t.Run("ok", func(t *testing.T) {
		server := NewServer(BuiltinEventHandler{}, nil)
		addr := ":" + nextPort()
		go server.Run(addr)

		time.Sleep(100 * time.Millisecond)
		conn, err := net.Dial("tcp", "localhost"+addr)
		if err != nil {
			as.NoError(err)
			return
		}
		_, _, err = NewClientFromConn(BuiltinEventHandler{}, &ClientOption{
			RequestHeader: http.Header{
				"Host": []string{"localhost"},
			},
		}, conn)
		as.NoError(err)
	})

	t.Run("fail", func(t *testing.T) {
		server := NewServer(BuiltinEventHandler{}, nil)
		addr := ":" + nextPort()
		go server.Run(addr)

		time.Sleep(100 * time.Millisecond)
		conn, _ := net.Pipe()
		_, _, err := NewClientFromConn(BuiltinEventHandler{}, &ClientOption{}, conn)
		as.Error(err)
	})
}

func TestClientHandshake(t *testing.T) {
	var as = assert.New(t)
	option := &ClientOption{
		RequestHeader:     http.Header{},
		PermessageDeflate: PermessageDeflate{Enabled: true},
	}
	option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
	option = initClientOption(option)
	srv, cli := net.Pipe()
	var d = &connector{
		option:          option,
		conn:            cli,
		eventHandler:    new(BuiltinEventHandler),
		secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
	}

	go func() {
		var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
		for {
			var buf = make([]byte, 1024)
			srv.Read(buf)
			srv.Write([]byte(text))
		}
	}()
	if _, _, err := d.handshake(); err != nil {
		as.NoError(err)
		return
	}
}

func TestClientHandshakeFail(t *testing.T) {
	var as = assert.New(t)

	t.Run("", func(t *testing.T) {
		option := &ClientOption{
			RequestHeader:     http.Header{},
			PermessageDeflate: PermessageDeflate{Enabled: true},
		}
		option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 400 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		option := &ClientOption{
			PermessageDeflate: PermessageDeflate{Enabled: true},
			RequestHeader:     http.Header{},
		}
		option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upg: rade\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		option := &ClientOption{
			PermessageDeflate: PermessageDeflate{Enabled: true},
			RequestHeader:     http.Header{},
		}
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: _ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		option := &ClientOption{
			PermessageDeflate: PermessageDeflate{Enabled: true},
			RequestHeader:     http.Header{},
		}
		option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket1\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		option := &ClientOption{
			PermessageDeflate: PermessageDeflate{Enabled: true},
			RequestHeader:     http.Header{},
		}
		option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket1\r\nConnection: Upgrade1\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.Error(err)
	})

	t.Run("deflate", func(t *testing.T) {
		option := &ClientOption{
			PermessageDeflate: PermessageDeflate{Enabled: true},
			RequestHeader:     http.Header{},
		}
		option.RequestHeader.Set(internal.SecWebSocketKey.Key, "1fTfP/qALD+eAWcU80P0bg==")
		option = initClientOption(option)
		srv, cli := net.Pipe()
		var d = &connector{
			option:          option,
			conn:            cli,
			secWebsocketKey: "1fTfP/qALD+eAWcU80P0bg==",
			eventHandler:    new(BuiltinEventHandler),
		}

		go func() {
			var text = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Extensions: permessage-deflate\r\nSec-WebSocket-Accept: ygR8UkmG67DM75dkgZzwplwlEEo=\r\n\r\n"
			for {
				var buf = make([]byte, 1024)
				srv.Read(buf)
				srv.Write([]byte(text))
			}
		}()
		_, _, err := d.handshake()
		as.NoError(err)
	})
}

var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy
MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu
IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0
x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l
cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa
gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM
lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud
DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM
QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf
SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871
2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi
WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB
irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk
M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1
DNn00G8C6ttLoGU2snyk
-----END CERTIFICATE-----
`)

var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ
KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a
SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ
6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd
JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY
0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu
jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH
xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS
aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV
1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef
i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp
GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP
ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb
Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF
XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S
TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk
Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt
qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2
Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6
nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti
fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB
L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk
HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj
Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X
BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck=
-----END RSA PRIVATE KEY-----
`)

func TestNewClientWSS(t *testing.T) {
	var as = assert.New(t)

	addr := "127.0.0.1:" + nextPort()
	srv := NewServer(new(BuiltinEventHandler), nil)
	certs, _ := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM)
	listener, err := tls.Listen("tcp", addr, &tls.Config{Certificates: []tls.Certificate{certs}})
	if err != nil {
		as.NoError(err)
		return
	}
	go func() {
		if err := srv.RunListener(listener); err != nil {
			as.NoError(err)
			return
		}
	}()

	t.Run("", func(t *testing.T) {
		opts := &ClientOption{
			Addr:      "wss://" + addr,
			TlsConfig: &tls.Config{InsecureSkipVerify: true},
		}
		_, resp, err := NewClient(&BuiltinEventHandler{}, opts)
		as.NoError(err)
		as.Equal(opts.TlsConfig.ServerName, resp.Request.URL.Hostname())
	})

	t.Run("", func(t *testing.T) {
		opts := &ClientOption{
			Addr: "wss://" + addr,
		}
		_, _, err = NewClient(&BuiltinEventHandler{}, opts)
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		_, _, err = NewClient(&BuiltinEventHandler{}, &ClientOption{
			Addr:      "wss://" + addr,
			TlsConfig: &tls.Config{InsecureSkipVerify: true},
			NewDialer: func() (Dialer, error) {
				return nil, io.EOF
			},
		})
		as.Error(err)
	})

	t.Run("", func(t *testing.T) {
		_, _, err = NewClient(&BuiltinEventHandler{}, &ClientOption{
			Addr:      "wss://" + addr + "/%x",
			TlsConfig: &tls.Config{InsecureSkipVerify: true},
			NewDialer: func() (Dialer, error) {
				return nil, io.EOF
			},
		})
		as.Error(err)
	})
}

func TestNewClient_WriteRequest(t *testing.T) {
	t.Run("", func(t *testing.T) {
		srv, conn := net.Pipe()
		go func() {
			for {
				var p = make([]byte, 1024)
				srv.Read(p)
				time.Sleep(time.Second)
			}
		}()
		c := connector{conn: conn, option: &ClientOption{
			Addr:             "ws://127.0.0.1/a=%",
			HandshakeTimeout: 100 * time.Millisecond,
		}}
		_, _, err := c.request()
		assert.Error(t, err)
	})

	t.Run("", func(t *testing.T) {
		_, conn := net.Pipe()
		c := connector{conn: conn, option: &ClientOption{
			Addr:             "ws://127.0.0.1:8080/",
			HandshakeTimeout: 100 * time.Millisecond,
		}}
		_, _, err := c.request()
		assert.Error(t, err)
	})

	t.Run("", func(t *testing.T) {
		_, conn := net.Pipe()
		c := connector{
			conn:   conn,
			option: &ClientOption{Addr: "ws://127.0.0.1/a=%"},
		}
		_ = conn.Close()
		_, _, err := c.handshake()
		assert.Error(t, err)
	})
}
