package util

import (
	stdtls "crypto/tls"
	"encoding/hex"
	"net"

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

func NewUTLSClient(conn net.Conn, config *stdtls.Config, clientHelloID tls.ClientHelloID) *tls.UConn {
	return tls.UClient(conn, NewUTLSConfig(config), clientHelloID)
}

func NewUTLSChromeClient(conn net.Conn, config *stdtls.Config) *tls.UConn {
	return NewUTLSClient(conn, config, tls.HelloChrome_Auto)
}

func NewUTLSWebSocketClient(conn net.Conn, config *stdtls.Config, useH2 bool) (*tls.UConn, error) {
	utlsConfig := NewLegacyUTLSConfig(config)
	if useH2 {
		return tls.UClient(conn, utlsConfig, tls.HelloChrome_Auto), nil
	}

	client := tls.UClient(conn, utlsConfig, tls.HelloCustom)
	spec, err := NewWebSocketClientHelloSpec()
	if err != nil {
		return nil, err
	}
	if err := client.ApplyPreset(spec); err != nil {
		return nil, err
	}
	return client, nil
}

func NewLegacyUTLSConfig(config *stdtls.Config) *tls.Config {
	if config == nil {
		return &tls.Config{}
	}

	cfg := &tls.Config{
		InsecureSkipVerify: config.InsecureSkipVerify,
		ServerName:         config.ServerName,
		ClientAuth:         tls.ClientAuthType(config.ClientAuth),
		ClientCAs:          config.ClientCAs,
		RootCAs:            config.RootCAs,
	}

	cfg.Certificates = make([]tls.Certificate, 0, len(config.Certificates))
	for _, cert := range config.Certificates {
		cfg.Certificates = append(cfg.Certificates, tls.Certificate{
			Certificate:                 cert.Certificate,
			PrivateKey:                  cert.PrivateKey,
			OCSPStaple:                  cert.OCSPStaple,
			SignedCertificateTimestamps: cert.SignedCertificateTimestamps,
			Leaf:                        cert.Leaf,
		})
	}

	return cfg
}

func NewUTLSConfig(config *stdtls.Config) *tls.Config {
	if config == nil {
		return &tls.Config{}
	}

	cfg := &tls.Config{
		Rand:                        config.Rand,
		Time:                        config.Time,
		RootCAs:                     config.RootCAs,
		NextProtos:                  append([]string(nil), config.NextProtos...),
		ServerName:                  config.ServerName,
		ClientAuth:                  tls.ClientAuthType(config.ClientAuth),
		ClientCAs:                   config.ClientCAs,
		InsecureSkipVerify:          config.InsecureSkipVerify,
		CipherSuites:                append([]uint16(nil), config.CipherSuites...),
		SessionTicketsDisabled:      config.SessionTicketsDisabled,
		MinVersion:                  config.MinVersion,
		MaxVersion:                  config.MaxVersion,
		DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled,
		Renegotiation:               tls.RenegotiationSupport(config.Renegotiation),
		KeyLogWriter:                config.KeyLogWriter,
		VerifyPeerCertificate:       config.VerifyPeerCertificate,
	}

	cfg.Certificates = make([]tls.Certificate, 0, len(config.Certificates))
	for _, cert := range config.Certificates {
		cfg.Certificates = append(cfg.Certificates, toUTLSCertificate(cert))
	}

	cfg.CurvePreferences = make([]tls.CurveID, 0, len(config.CurvePreferences))
	for _, curve := range config.CurvePreferences {
		cfg.CurvePreferences = append(cfg.CurvePreferences, tls.CurveID(curve))
	}

	if config.VerifyConnection != nil {
		cfg.VerifyConnection = func(state tls.ConnectionState) error {
			return config.VerifyConnection(toStdTLSConnectionState(state))
		}
	}

	return cfg
}

func toUTLSCertificate(cert stdtls.Certificate) tls.Certificate {
	signatureAlgorithms := make([]tls.SignatureScheme, 0, len(cert.SupportedSignatureAlgorithms))
	for _, sig := range cert.SupportedSignatureAlgorithms {
		signatureAlgorithms = append(signatureAlgorithms, tls.SignatureScheme(sig))
	}

	return tls.Certificate{
		Certificate:                  cert.Certificate,
		PrivateKey:                   cert.PrivateKey,
		SupportedSignatureAlgorithms: signatureAlgorithms,
		OCSPStaple:                   cert.OCSPStaple,
		SignedCertificateTimestamps:  cert.SignedCertificateTimestamps,
		Leaf:                         cert.Leaf,
	}
}

func toStdTLSConnectionState(state tls.ConnectionState) stdtls.ConnectionState {
	return stdtls.ConnectionState{
		Version:                     state.Version,
		HandshakeComplete:           state.HandshakeComplete,
		DidResume:                   state.DidResume,
		CipherSuite:                 state.CipherSuite,
		NegotiatedProtocol:          state.NegotiatedProtocol,
		NegotiatedProtocolIsMutual:  state.NegotiatedProtocolIsMutual,
		ServerName:                  state.ServerName,
		PeerCertificates:            state.PeerCertificates,
		VerifiedChains:              state.VerifiedChains,
		SignedCertificateTimestamps: state.SignedCertificateTimestamps,
		OCSPResponse:                state.OCSPResponse,
		TLSUnique:                   state.TLSUnique,
		ECHAccepted:                 state.ECHAccepted,
	}
}

func NewWebSocketClientHelloSpec() (*tls.ClientHelloSpec, error) {
	fingerprinter := &tls.Fingerprinter{}
	rawCapturedClientHelloBytes, _ := hex.DecodeString("16030107b8010007b403036252c641762f4a4f55d93dcbe1a01e62839ba1b48e9082896737ca23dc6bd02120c719a58f491c56be1f21fcd68bd097f2ee5a370c576a7c512aba46b7ed73841a0020eaea130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f00350100074baaaa0000000a000c000a5a5a11ec001d00170018000d0012001004030804040105030805050108060601ff0100010000170000002b0007065a5a03040303001b00030200020010000b000908687474702f312e31000b00020100002d00020101fe0d00ba0000010001be0020a5fb3e1a8aa228913bdf2dc9c52670860d2032a04d71140d6dc08f4cb41b2a1c0090420727529f82e9a8b097215adb52126184c638a98f0a6f8121b04edb939a7ff2a9564b44a917160ceb0a41f7a2dc12b89c622bc6687a21759c813858c81fe2e9024b02c6501c4b4c9e87ed2bd1538a420b6045a0785a66b044e53260e19b6f09042b903fa6758ce571dff16b91e95a19dbe5da0a46cab946036f48ad1394534866ee874f8dc75b3e4efa444be660262f003304ef04ed5a5a00010011ec04c098b01e12861f0d1738ebdb83b5fb876e7a1382f50149279a62479352b110f9791d1710c99cd988348175a20884650682e5e6a48bd2b27bb52360882bd84c6f97b6bc142c300ba7ab420bbec1872c13ba6970501e2b473f5c325da09a1dda800060d3c14454a95e6a67ddf28be60140cc3b07d393826b8babaf9a074b97bfa6fb8a2da784e98c5a332c62d7a60b7ac5b85e6606d128a4e9d89056680c6ff3ad5c3a3018218006db0fd81843e4ccb3f3e42ec858554be50aaa5b9afb613a1c07a631551706c4840bdb11a41564db56cdd2e58d1c619fc34567b74a2c54755f4174a0d589bf5938388a0976456c2a2aca2c08b2af11a2b550da9b22e4a39e2a7759c887e884b362b0b0e8394a149b44e82b6f5e680cd280cd2167953c94b111c20c4d028b2fc0926ac1a0454977fdf4a6876a555048a0c8b23531215f8543b5af5a2a69fc4151a870f481204e3a1535ec35321697d0ea0a470239e7b40b99e9b7ee01205bf567c42479c137c346b8af4dd825d4324c6d34875dd50b1841a78c42b79075af259a0f45252f311997f39902337088ee6817565b2e63858924a2354d86b1ba77bfa324339ec4a72663134db7227e652aea43700f751f7f519c5a66a036e0274a48c58716a3eb8a6de4730d19d534dd52ae83b9a7f07ccbc8d63b073a5127e3170dc58031524b81b611c6c4cf2aaba01fc55747b643fb417c85c0a21aa823709a037e418fc8e9c16c51a0c105c07d7c2f56dbcd4516a17a10036531768fb04300f0cdcc7c5135517b1643c3204b47575b97d0a513287985a0353523d93d511b694d6bb5ecc0698823855a606b83c77723808dd6737c9129a5c50457e71a063f927299214e902861e89a81423715b60717e8551b29126f857499810a0328d7182b538276d15517aa21b828ba7afcc13c1cc4cd1574a1c97bcfe4490f766d0e95a0c2911bc0d7a47161bcf5c344558314e7a1ab6c4227950b76c3319acaa53a7776a91e3b62ecd38f0732504674247947855a95892cc43b7f56a09f0c4281976e2b73820ff48180f054f482638078b08cf01d2b221a35214002b5cc8e394a414230a4f562af739e70ec540c260c651009a0616c5830658e0cbb2d2a2f94dab9f539a9ef19750f4925f7106a3a9c0a68e4a7d442202678beeef25b92c8709d93133c08728603ac900719db6615d294ad1aa965546a0c655a6891ab8e9fea31b7262dbba5af94372262934a4feac79ad72a3277aa7e97ab48f9a06f6857d26a2d48306b80a4bb600785f5b26f00202d2eb6af0226774e93615a484fa42a91bfe6034e890f6642c95a979d8df8a39ab10977846b1dc1998b51a8dde62975dc54bf92ada8dc0d7c0a020f429d4ff0b11ba95b6e076ec0443d01f0cb963441ddca9244631b776a029f874b799534c9c14808863c756b2d8a0309a0fab26bc37db05601cd5741acf9acc4c84a9e6b76b1e8282908a8d2268562e72109c3c8cb93507e1accf48723a056bd4acab4317771d4e432216a0a39377792469757a8c186d8085078aa2a8bce201628bad8c7ce98bb41dc56d6715a1dc3cb1cab48cb5a8f18d04a651b9efdd751bd199c266aac08726177bb432ecba334b06e516c12449846dcf90a4080248e2b15cb435e021ee8c4a8c17c095babf8011d67e0a5f16e25dc7357fc1c78e5884fe32598695589294e9c431c94ac2f5d2053757f9a905df36b001d002022b1fc0ef89cbfbd8f197c3e230879a2e71c155a1a6cb1bc33136cf93380c41d00000011000f00000c7373682e6576616e2e72756e0012000000230000000500050100000000baba0001000029010b00e600e0d1ae9857a5731dd0d5ca9d726bf57748ff9f57a7bb61ee7cc84e0f06358291fd2694914ab4ab3abc95fec3a1b393f458cca49eddd4dc24c896429eba5464d54ed59c562be84fe5660c8f74263db3c5eefbd36f2c667b9bd8219e20d84162a545978844bce25f2dc0f1c56f032bcee7f0bcd9fc762cfa115f368d0fe3b2e03fce32673bfdafd419eb8bc36bcf9c4a0d49c8d750ea7373e41c2ac13eb97f8fdb6c619742e98951c6447b76b263e0040ae874c7bd486421af99541b3b40e5b6eb3c0ae555dc1dfa7448aee19f05e54f70eb6655a91eb03eb93015a45eb29adc73d82437927e002120022bd91157ac53b55c75c03be16f0bc0040f38fa0085949f0157f8e7db794cc5")
	spec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
	return spec, err
}
