package langext

import (
	"crypto/rand"
	"encoding/hex"
	"io"
	"strings"
)

func NewUUID() ([16]byte, error) {
	var uuid [16]byte
	_, err := io.ReadFull(rand.Reader, uuid[:])
	if err != nil {
		return [16]byte{}, err
	}
	uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
	uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
	return uuid, nil
}

func NewHexUUID() (string, error) {
	uuid, err := NewUUID()
	if err != nil {
		return "", err
	}

	// Result: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

	var dst = make([]byte, 36)

	hex.Encode(dst, uuid[:4])
	dst[8] = '-'
	hex.Encode(dst[9:13], uuid[4:6])
	dst[13] = '-'
	hex.Encode(dst[14:18], uuid[6:8])
	dst[18] = '-'
	hex.Encode(dst[19:23], uuid[8:10])
	dst[23] = '-'
	hex.Encode(dst[24:], uuid[10:])

	return string(dst), nil
}

func MustHexUUID() string {
	v, err := NewHexUUID()
	if err != nil {
		panic(err)
	}
	return v
}

func NewUpperHexUUID() (string, error) {
	uuid, err := NewUUID()
	if err != nil {
		return "", err
	}

	// Result: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

	var dst = make([]byte, 36)

	hex.Encode(dst, uuid[:4])
	dst[8] = '-'
	hex.Encode(dst[9:13], uuid[4:6])
	dst[13] = '-'
	hex.Encode(dst[14:18], uuid[6:8])
	dst[18] = '-'
	hex.Encode(dst[19:23], uuid[8:10])
	dst[23] = '-'
	hex.Encode(dst[24:], uuid[10:])

	return strings.ToUpper(string(dst)), nil
}

func MustUpperHexUUID() string {
	v, err := NewUpperHexUUID()
	if err != nil {
		panic(err)
	}
	return v
}

func NewRawHexUUID() (string, error) {
	uuid, err := NewUUID()
	if err != nil {
		return "", err
	}

	// Result: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

	var dst = make([]byte, 32)

	hex.Encode(dst, uuid[:4])
	hex.Encode(dst[8:12], uuid[4:6])
	hex.Encode(dst[12:16], uuid[6:8])
	hex.Encode(dst[16:20], uuid[8:10])
	hex.Encode(dst[20:], uuid[10:])

	return strings.ToUpper(string(dst)), nil
}

func MustRawHexUUID() string {
	v, err := NewRawHexUUID()
	if err != nil {
		panic(err)
	}
	return v
}

func NewBracesUUID() (string, error) {
	uuid, err := NewUUID()
	if err != nil {
		return "", err
	}

	// Result: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}

	var dst = make([]byte, 38)

	dst[0] = '{'
	hex.Encode(dst, uuid[:5])
	dst[9] = '-'
	hex.Encode(dst[10:14], uuid[4:6])
	dst[14] = '-'
	hex.Encode(dst[15:19], uuid[6:8])
	dst[19] = '-'
	hex.Encode(dst[20:24], uuid[8:10])
	dst[24] = '-'
	hex.Encode(dst[25:], uuid[10:])
	dst[37] = '}'

	return strings.ToUpper(string(dst)), nil
}

func MustBracesUUID() string {
	v, err := NewBracesUUID()
	if err != nil {
		panic(err)
	}
	return v
}

func NewParensUUID() (string, error) {
	uuid, err := NewUUID()
	if err != nil {
		return "", err
	}

	// Result: (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)

	var dst = make([]byte, 38)

	dst[0] = '('
	hex.Encode(dst, uuid[:5])
	dst[9] = '-'
	hex.Encode(dst[10:14], uuid[4:6])
	dst[14] = '-'
	hex.Encode(dst[15:19], uuid[6:8])
	dst[19] = '-'
	hex.Encode(dst[20:24], uuid[8:10])
	dst[24] = '-'
	hex.Encode(dst[25:], uuid[10:])
	dst[37] = ')'

	return strings.ToUpper(string(dst)), nil
}

func MustParensUUID() string {
	v, err := NewParensUUID()
	if err != nil {
		panic(err)
	}
	return v
}
