package util

import (
	stdtls "crypto/tls"
	"net"
	"testing"

	tls "github.com/refraction-networking/utls"
)

func TestNewUTLSConfigPreservesClientSettings(t *testing.T) {
	called := false
	cfg := NewUTLSConfig(&stdtls.Config{
		ServerName:                  "example.com",
		InsecureSkipVerify:          true,
		NextProtos:                  []string{"h2", "http/1.1"},
		MinVersion:                  stdtls.VersionTLS12,
		MaxVersion:                  stdtls.VersionTLS13,
		CipherSuites:                []uint16{stdtls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
		CurvePreferences:            []stdtls.CurveID{stdtls.X25519, stdtls.CurveP256},
		DynamicRecordSizingDisabled: true,
		Renegotiation:               stdtls.RenegotiateOnceAsClient,
		VerifyConnection: func(state stdtls.ConnectionState) error {
			called = true
			if state.ServerName != "example.com" {
				t.Fatalf("ServerName = %q, want example.com", state.ServerName)
			}
			return nil
		},
	})

	if cfg.ServerName != "example.com" {
		t.Fatalf("ServerName = %q, want example.com", cfg.ServerName)
	}
	if !cfg.InsecureSkipVerify {
		t.Fatal("InsecureSkipVerify was not preserved")
	}
	if len(cfg.NextProtos) != 2 || cfg.NextProtos[0] != "h2" || cfg.NextProtos[1] != "http/1.1" {
		t.Fatalf("NextProtos = %v", cfg.NextProtos)
	}
	if cfg.MinVersion != tls.VersionTLS12 || cfg.MaxVersion != tls.VersionTLS13 {
		t.Fatalf("version range = %x-%x", cfg.MinVersion, cfg.MaxVersion)
	}
	if len(cfg.CipherSuites) != 1 || cfg.CipherSuites[0] != tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 {
		t.Fatalf("CipherSuites = %v", cfg.CipherSuites)
	}
	if len(cfg.CurvePreferences) != 2 || cfg.CurvePreferences[0] != tls.X25519 || cfg.CurvePreferences[1] != tls.CurveP256 {
		t.Fatalf("CurvePreferences = %v", cfg.CurvePreferences)
	}
	if !cfg.DynamicRecordSizingDisabled {
		t.Fatal("DynamicRecordSizingDisabled was not preserved")
	}
	if cfg.Renegotiation != tls.RenegotiateOnceAsClient {
		t.Fatalf("Renegotiation = %v", cfg.Renegotiation)
	}
	if cfg.VerifyConnection == nil {
		t.Fatal("VerifyConnection was not preserved")
	}
	if err := cfg.VerifyConnection(tls.ConnectionState{ServerName: "example.com"}); err != nil {
		t.Fatal(err)
	}
	if !called {
		t.Fatal("VerifyConnection wrapper was not called")
	}
}

func TestNewUTLSChromeClientBuildsChromeHello(t *testing.T) {
	c1, c2 := net.Pipe()
	defer c1.Close()
	defer c2.Close()

	client := NewUTLSChromeClient(c1, &stdtls.Config{ServerName: "example.com"})
	if client.ClientHelloID != tls.HelloChrome_Auto {
		t.Fatalf("ClientHelloID = %+v, want %+v", client.ClientHelloID, tls.HelloChrome_Auto)
	}
	if err := client.BuildHandshakeStateWithoutSession(); err != nil {
		t.Fatal(err)
	}
	if len(client.Extensions) == 0 {
		t.Fatal("Chrome ClientHello has no extensions")
	}
}

func TestNewUTLSWebSocketClientAppliesCustomSpec(t *testing.T) {
	c1, c2 := net.Pipe()
	defer c1.Close()
	defer c2.Close()

	client, err := NewUTLSWebSocketClient(c1, &stdtls.Config{
		ServerName: "example.com",
		NextProtos: []string{"h2"},
		MinVersion: stdtls.VersionTLS13,
	}, false)
	if err != nil {
		t.Fatal(err)
	}
	if client.ClientHelloID != tls.HelloCustom {
		t.Fatalf("ClientHelloID = %+v, want %+v", client.ClientHelloID, tls.HelloCustom)
	}
	if len(client.Extensions) == 0 {
		t.Fatal("custom WebSocket ClientHello preset has no extensions")
	}
	if err := client.BuildHandshakeStateWithoutSession(); err != nil {
		t.Fatal(err)
	}
	if len(client.HandshakeState.Hello.AlpnProtocols) != 1 || client.HandshakeState.Hello.AlpnProtocols[0] != "http/1.1" {
		t.Fatalf("ALPN = %v, want [http/1.1]", client.HandshakeState.Hello.AlpnProtocols)
	}
}
