package encryption

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
)

// NewClient new client from encryption string
// maybe return a nil *ClientInstance without any error, that means don't need to encrypt
func NewClient(encryption string) (*ClientInstance, error) {
	switch encryption {
	case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
		return nil, nil
	}
	if s := strings.Split(encryption, "."); len(s) >= 4 && s[0] == "mlkem768x25519plus" {
		var xorMode uint32
		switch s[1] {
		case "native":
		case "xorpub":
			xorMode = 1
		case "random":
			xorMode = 2
		default:
			return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
		}
		var seconds uint32
		switch s[2] {
		case "1rtt":
		case "0rtt":
			seconds = 1
		default:
			return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
		}
		var nfsPKeysBytes [][]byte
		var paddings []string
		for _, r := range s[3:] {
			if len(r) < 20 {
				paddings = append(paddings, r)
				continue
			}
			b, err := base64.RawURLEncoding.DecodeString(r)
			if err != nil {
				return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
			}
			if len(b) != X25519PasswordSize && len(b) != MLKEM768ClientLength {
				return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
			}
			nfsPKeysBytes = append(nfsPKeysBytes, b)
		}
		padding := strings.Join(paddings, ".")
		client := &ClientInstance{}
		if err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {
			return nil, fmt.Errorf("failed to use encryption: %w", err)
		}
		return client, nil
	}
	return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}

// NewServer new server from decryption string
// maybe return a nil *ServerInstance without any error, that means don't need to decrypt
func NewServer(decryption string) (*ServerInstance, error) {
	switch decryption {
	case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
		return nil, nil
	}
	if s := strings.Split(decryption, "."); len(s) >= 4 && s[0] == "mlkem768x25519plus" {
		var xorMode uint32
		switch s[1] {
		case "native":
		case "xorpub":
			xorMode = 1
		case "random":
			xorMode = 2
		default:
			return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
		}
		t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2)
		i, err := strconv.Atoi(t[0])
		if err != nil {
			return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
		}
		secondsFrom := int64(i)
		secondsTo := int64(0)
		if len(t) == 2 {
			i, err = strconv.Atoi(t[1])
			if err != nil {
				return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
			}
			secondsTo = int64(i)
		}
		var nfsSKeysBytes [][]byte
		var paddings []string
		for _, r := range s[3:] {
			if len(r) < 20 {
				paddings = append(paddings, r)
				continue
			}
			b, err := base64.RawURLEncoding.DecodeString(r)
			if err != nil {
				return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
			}
			if len(b) != X25519PrivateKeySize && len(b) != MLKEM768SeedLength {
				return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
			}
			nfsSKeysBytes = append(nfsSKeysBytes, b)
		}
		padding := strings.Join(paddings, ".")
		server := &ServerInstance{}
		if err := server.Init(nfsSKeysBytes, xorMode, secondsFrom, secondsTo, padding); err != nil {
			return nil, fmt.Errorf("failed to use decryption: %w", err)
		}
		return server, nil
	}
	return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
