package token

import (
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"strings"
)

const TokenLength = 32 // 32 bytes = 256 bits per token

// Tokens holds the 3 authentication tokens
type Tokens struct {
	Token1 string `json:"token1"`
	Token2 string `json:"token2"`
	Token3 string `json:"token3"`
}

// Generate creates 3 cryptographically random tokens
func Generate() (*Tokens, error) {
	t := &Tokens{}
	var err error

	t.Token1, err = randomToken()
	if err != nil {
		return nil, fmt.Errorf("generating token 1: %w", err)
	}
	t.Token2, err = randomToken()
	if err != nil {
		return nil, fmt.Errorf("generating token 2: %w", err)
	}
	t.Token3, err = randomToken()
	if err != nil {
		return nil, fmt.Errorf("generating token 3: %w", err)
	}

	return t, nil
}

// Hash computes the SHA256 hash of the 3 tokens combined
func (t *Tokens) Hash() string {
	combined := strings.Join([]string{t.Token1, t.Token2, t.Token3}, ":")
	sum := sha256.Sum256([]byte(combined))
	return fmt.Sprintf("%x", sum)
}

// HashFromString computes the hash from a pre-combined token string
func HashFromTokens(token1, token2, token3 string) string {
	t := &Tokens{Token1: token1, Token2: token2, Token3: token3}
	return t.Hash()
}

func randomToken() (string, error) {
	b := make([]byte, TokenLength)
	if _, err := rand.Read(b); err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}
