package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"os"
)

const moduleKeyEnv = "KANBAN_MODULE_KEY"

// moduleKey derives a 32-byte AES key from the KANBAN_MODULE_KEY env var.
// Returns (key, true) when present; (zero, false) when missing — callers
// must treat that as "module dispatcher disabled".
func moduleKey() ([32]byte, bool) {
	v := os.Getenv(moduleKeyEnv)
	if v == "" {
		return [32]byte{}, false
	}
	return sha256.Sum256([]byte(v)), true
}

// encryptConfig encrypts a JSON config blob with AES-GCM. Returns the
// ciphertext and the 12-byte nonce. Caller persists both columns.
func encryptConfig(plain []byte) (cipherOut, nonce []byte, err error) {
	key, ok := moduleKey()
	if !ok {
		return nil, nil, fmt.Errorf("%s not set; cannot encrypt module config", moduleKeyEnv)
	}
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, nil, err
	}
	nonce = make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return nil, nil, err
	}
	cipherOut = gcm.Seal(nil, nonce, plain, nil)
	return cipherOut, nonce, nil
}

// decryptConfig is the inverse of encryptConfig.
func decryptConfig(cipherIn, nonce []byte) ([]byte, error) {
	key, ok := moduleKey()
	if !ok {
		return nil, fmt.Errorf("%s not set; cannot decrypt module config", moduleKeyEnv)
	}
	if len(nonce) == 0 {
		return nil, errors.New("nonce empty")
	}
	block, err := aes.NewCipher(key[:])
	if err != nil {
		return nil, err
	}
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}
	return gcm.Open(nil, nonce, cipherIn, nil)
}
