// Package lthn implements the LTHN quasi-salted hash algorithm (RFC-0004).
//
// LTHN produces deterministic, verifiable hashes without requiring separate salt
// storage. The salt is derived from the input itself through:
//  1. Reversing the input string
//  2. Applying "leet speak" style character substitutions
//
// The final hash is: SHA256(input || derived_salt)
//
// This is suitable for content identifiers, cache keys, and deduplication.
// NOT suitable for password hashing - use bcrypt, Argon2, or scrypt instead.
//
// Example:
//
//	hash := lthn.Hash("hello")
//	valid := lthn.Verify("hello", hash)  // true
package lthn

import (
	"crypto/sha256"
	"encoding/hex"
)

// keyMap defines the character substitutions for quasi-salt derivation.
// These are inspired by "leet speak" conventions for letter-number substitution.
// The mapping is bidirectional for most characters but NOT fully symmetric.
var keyMap = map[rune]rune{
	'o': '0', // letter O -> zero
	'l': '1', // letter L -> one
	'e': '3', // letter E -> three
	'a': '4', // letter A -> four
	's': 'z', // letter S -> Z
	't': '7', // letter T -> seven
	'0': 'o', // zero -> letter O
	'1': 'l', // one -> letter L
	'3': 'e', // three -> letter E
	'4': 'a', // four -> letter A
	'7': 't', // seven -> letter T
}

// SetKeyMap replaces the default character substitution map.
// Use this to customize the quasi-salt derivation for specific applications.
// Changes affect all subsequent Hash and Verify calls.
func SetKeyMap(newKeyMap map[rune]rune) {
	keyMap = newKeyMap
}

// GetKeyMap returns the current character substitution map.
func GetKeyMap() map[rune]rune {
	return keyMap
}

// Hash computes the LTHN hash of the input string.
//
// The algorithm:
//  1. Derive a quasi-salt by reversing the input and applying character substitutions
//  2. Concatenate: input + salt
//  3. Compute SHA-256 of the concatenated string
//  4. Return the hex-encoded digest (64 characters, lowercase)
//
// The same input always produces the same hash, enabling verification
// without storing a separate salt value.
func Hash(input string) string {
	salt := createSalt(input)
	hash := sha256.Sum256([]byte(input + salt))
	return hex.EncodeToString(hash[:])
}

// createSalt derives a quasi-salt by reversing the input and applying substitutions.
// For example: "hello" -> reversed "olleh" -> substituted "011eh"
func createSalt(input string) string {
	if input == "" {
		return ""
	}
	runes := []rune(input)
	salt := make([]rune, len(runes))
	for i := 0; i < len(runes); i++ {
		char := runes[len(runes)-1-i]
		if replacement, ok := keyMap[char]; ok {
			salt[i] = replacement
		} else {
			salt[i] = char
		}
	}
	return string(salt)
}

// Verify checks if an input string produces the given hash.
// Returns true if Hash(input) equals the provided hash value.
// Uses direct string comparison - for security-critical applications,
// consider using constant-time comparison.
func Verify(input string, hash string) bool {
	return Hash(input) == hash
}
