// Package crypt provides cryptographic functions to the Core application.
// It wraps the Enchantrix library, providing a Core-compatible service layer
// for hashing, checksums, RSA, and PGP operations.
package crypt

import (
	"fmt"
	"io"

	"forge.lthn.ai/core/gui/pkg/core"
	"github.com/Snider/Enchantrix/pkg/crypt"
)

// HandleIPCEvents processes IPC messages for the crypt service.
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
	switch msg.(type) {
	case core.ActionServiceStartup:
		// Crypt is stateless, no startup needed.
		return nil
	default:
		if c.App != nil && c.App.Logger != nil {
			c.App.Logger.Debug("Crypt: Unhandled message type", "type", fmt.Sprintf("%T", msg))
		}
	}
	return nil
}

// Options holds configuration for the crypt service.
type Options struct{}

// Service provides cryptographic functions to the application.
// It delegates to Enchantrix for all cryptographic operations.
type Service struct {
	*core.ServiceRuntime[Options]
	enchantrix *crypt.Service
}

// HashType defines the supported hashing algorithms.
// Re-exported from Enchantrix for convenience.
type HashType = crypt.HashType

// Hash type constants re-exported from Enchantrix.
const (
	LTHN   HashType = crypt.LTHN
	SHA512 HashType = crypt.SHA512
	SHA256 HashType = crypt.SHA256
	SHA1   HashType = crypt.SHA1
	MD5    HashType = crypt.MD5
)

// newCryptService contains the common logic for initializing a Service struct.
func newCryptService() (*Service, error) {
	return &Service{
		enchantrix: crypt.NewService(),
	}, nil
}

// New is the constructor for static dependency injection.
// It creates a Service instance without initializing the core.ServiceRuntime field.
func New() (*Service, error) {
	return newCryptService()
}

// Register is the constructor for dynamic dependency injection (used with core.WithService).
// It creates a Service instance and initializes its core.ServiceRuntime field.
func Register(c *core.Core) (any, error) {
	s, err := newCryptService()
	if err != nil {
		return nil, err
	}
	s.ServiceRuntime = core.NewServiceRuntime(c, Options{})
	return s, nil
}

// --- Hashing ---

// Hash computes a hash of the payload using the specified algorithm.
func (s *Service) Hash(lib HashType, payload string) string {
	return s.enchantrix.Hash(lib, payload)
}

// IsHashAlgo checks if the given string is a valid hash algorithm.
func (s *Service) IsHashAlgo(algo string) bool {
	return s.enchantrix.IsHashAlgo(algo)
}

// --- Checksums ---

// Luhn validates a number using the Luhn algorithm.
func (s *Service) Luhn(payload string) bool {
	return s.enchantrix.Luhn(payload)
}

// Fletcher16 computes the Fletcher-16 checksum.
func (s *Service) Fletcher16(payload string) uint16 {
	return s.enchantrix.Fletcher16(payload)
}

// Fletcher32 computes the Fletcher-32 checksum.
func (s *Service) Fletcher32(payload string) uint32 {
	return s.enchantrix.Fletcher32(payload)
}

// Fletcher64 computes the Fletcher-64 checksum.
func (s *Service) Fletcher64(payload string) uint64 {
	return s.enchantrix.Fletcher64(payload)
}

// --- RSA ---

// GenerateRSAKeyPair generates an RSA key pair with the specified bit size.
// Returns PEM-encoded public and private keys.
func (s *Service) GenerateRSAKeyPair(bits int) (publicKey, privateKey string, err error) {
	pubBytes, privBytes, err := s.enchantrix.GenerateRSAKeyPair(bits)
	if err != nil {
		return "", "", err
	}
	return string(pubBytes), string(privBytes), nil
}

// EncryptRSA encrypts data using an RSA public key.
// Takes PEM-encoded public key and returns base64-encoded ciphertext.
func (s *Service) EncryptRSA(publicKeyPEM, plaintext string) (string, error) {
	ciphertext, err := s.enchantrix.EncryptRSA([]byte(publicKeyPEM), []byte(plaintext), nil)
	if err != nil {
		return "", err
	}
	return string(ciphertext), nil
}

// DecryptRSA decrypts data using an RSA private key.
// Takes PEM-encoded private key and ciphertext.
func (s *Service) DecryptRSA(privateKeyPEM, ciphertext string) (string, error) {
	plaintext, err := s.enchantrix.DecryptRSA([]byte(privateKeyPEM), []byte(ciphertext), nil)
	if err != nil {
		return "", err
	}
	return string(plaintext), nil
}

// --- PGP ---

// GeneratePGPKeyPair generates a PGP key pair.
// Note: Enchantrix PGP keys are not passphrase-protected. The comment parameter
// is used instead of passphrase for key metadata.
func (s *Service) GeneratePGPKeyPair(name, email, comment string) (publicKey, privateKey string, err error) {
	pubBytes, privBytes, err := s.enchantrix.GeneratePGPKeyPair(name, email, comment)
	if err != nil {
		return "", "", err
	}
	return string(pubBytes), string(privBytes), nil
}

// EncryptPGP encrypts data for a recipient and writes to the provided writer.
func (s *Service) EncryptPGP(writer io.Writer, recipientPublicKey, data string) error {
	ciphertext, err := s.enchantrix.EncryptPGP([]byte(recipientPublicKey), []byte(data))
	if err != nil {
		return err
	}
	_, err = writer.Write(ciphertext)
	return err
}

// EncryptPGPToString encrypts data for a recipient and returns the ciphertext.
func (s *Service) EncryptPGPToString(recipientPublicKey, data string) (string, error) {
	ciphertext, err := s.enchantrix.EncryptPGP([]byte(recipientPublicKey), []byte(data))
	if err != nil {
		return "", err
	}
	return string(ciphertext), nil
}

// DecryptPGP decrypts a PGP message.
// Note: Enchantrix does not support passphrase-protected keys for decryption.
func (s *Service) DecryptPGP(privateKey, message string) (string, error) {
	plaintext, err := s.enchantrix.DecryptPGP([]byte(privateKey), []byte(message))
	if err != nil {
		return "", err
	}
	return string(plaintext), nil
}

// SignPGP signs data with a PGP private key.
func (s *Service) SignPGP(privateKey, data string) (string, error) {
	signature, err := s.enchantrix.SignPGP([]byte(privateKey), []byte(data))
	if err != nil {
		return "", err
	}
	return string(signature), nil
}

// VerifyPGP verifies a PGP signature.
func (s *Service) VerifyPGP(publicKey, data, signature string) error {
	return s.enchantrix.VerifyPGP([]byte(publicKey), []byte(data), []byte(signature))
}

// SymmetricallyEncryptPGP encrypts data using a passphrase and writes to the provided writer.
func (s *Service) SymmetricallyEncryptPGP(writer io.Writer, data, passphrase string) error {
	ciphertext, err := s.enchantrix.SymmetricallyEncryptPGP([]byte(passphrase), []byte(data))
	if err != nil {
		return err
	}
	_, err = writer.Write(ciphertext)
	return err
}
