package chips

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"log/slog"
	"os"
)

func decryptToken(token string) (string, error) {
	if token == "" {
		return "", ErrEmptyQueryString
	}

	slog.Default().Info("Decrypting token", "token", token)

	// Decode base64 token
	ciphertext, err := base64.URLEncoding.DecodeString(token)
	if err != nil {
		return "", err
	}

	key := os.Getenv("CHIPS_CRYPT_KEY")
	if key == "" {
		return "", errors.New("CHIPS_CRYPT_KEY environment variable is not set")
	}

	// Create AES cipher
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	// Check minimum ciphertext length
	if len(ciphertext) < aes.BlockSize {
		return "", errors.New("ciphertext too short")
	}

	// Extract IV from beginning of ciphertext
	iv := ciphertext[:aes.BlockSize]
	ciphertext = ciphertext[aes.BlockSize:]

	// Create CBC decrypter
	mode := cipher.NewCBCDecrypter(block, iv)

	// Decrypt
	mode.CryptBlocks(ciphertext, ciphertext)

	// Remove PKCS7 padding
	plaintext, err := removePKCS7Padding(ciphertext)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}

func removePKCS7Padding(data []byte) ([]byte, error) {
	if len(data) == 0 {
		return nil, errors.New("empty data")
	}

	padding := int(data[len(data)-1])
	if padding > len(data) || padding == 0 {
		return nil, errors.New("invalid padding")
	}

	return data[:len(data)-padding], nil
}

func EncryptToken(plaintext string) (string, error) {
	if plaintext == "" {
		return "", errors.New("empty plaintext")
	}

	key := os.Getenv("CHIPS_CRYPT_KEY")
	if key == "" {
		return "", errors.New("CHIPS_CRYPT_KEY environment variable is not set")
	}

	// Create AES cipher
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	// Add PKCS7 padding
	paddedData := addPKCS7Padding([]byte(plaintext), aes.BlockSize)

	// Generate random IV
	iv := make([]byte, aes.BlockSize)
	if _, err := rand.Read(iv); err != nil {
		return "", err
	}

	// Create CBC encrypter
	mode := cipher.NewCBCEncrypter(block, iv)

	// Encrypt
	ciphertext := make([]byte, len(paddedData))
	mode.CryptBlocks(ciphertext, paddedData)

	// Prepend IV to ciphertext
	encrypted := append(iv, ciphertext...)

	// Encode to base64
	return base64.URLEncoding.EncodeToString(encrypted), nil
}

func addPKCS7Padding(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padText := make([]byte, padding)
	for i := range padText {
		padText[i] = byte(padding)
	}
	return append(data, padText...)
}
