// Package kms provides Key Management System (KMS) integrations for external
// key management providers (HashiCorp Vault, AWS KMS, etc.).
// This implements the KMS integration per ADR-012 through ADR-015.
package kms

import (
	"context"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"fmt"
	"time"

	"github.com/jfraeys/fetch_ml/internal/crypto/kms/config"
	"github.com/jfraeys/fetch_ml/internal/crypto/kms/providers"
)

// KMSProvider defines the interface for external KMS operations.
type KMSProvider interface {
	Encrypt(ctx context.Context, keyID string, plaintext []byte) ([]byte, error)
	Decrypt(ctx context.Context, keyID string, ciphertext []byte) ([]byte, error)
	CreateKey(ctx context.Context, tenantID string) (string, error)
	DisableKey(ctx context.Context, keyID string) error
	ScheduleKeyDeletion(ctx context.Context, keyID string, windowDays int) (time.Time, error)
	EnableKey(ctx context.Context, keyID string) error
	HealthCheck(ctx context.Context) error
	Close() error
}

// Provider type aliases to avoid duplication.
type ProviderType = config.ProviderType

const (
	ProviderTypeVault  = config.ProviderTypeVault
	ProviderTypeAWS    = config.ProviderTypeAWS
	ProviderTypeMemory = config.ProviderTypeMemory
)

// ProviderFactory creates KMS providers from configuration.
type ProviderFactory struct {
	config Config
}

// Config aliases.
type Config = config.Config
type VaultConfig = config.VaultConfig
type AWSConfig = config.AWSConfig
type CacheConfig = config.CacheConfig

// DefaultCacheConfig re-exports from config package.
func DefaultCacheConfig() CacheConfig {
	return config.DefaultCacheConfig()
}

// CreateProvider instantiates a KMS provider based on the configuration.
func (f *ProviderFactory) CreateProvider() (KMSProvider, error) {
	switch f.config.Provider {
	case ProviderTypeVault:
		return providers.NewVaultProvider(f.config.Vault)
	case ProviderTypeAWS:
		return providers.NewAWSProvider(f.config.AWS)
	case ProviderTypeMemory:
		return NewMemoryProvider(), nil
	default:
		return nil, fmt.Errorf("unsupported KMS provider: %s", f.config.Provider)
	}
}

// MemoryProvider implements KMSProvider for development/testing.
// Root keys are stored in-memory. NOT for production use.
type MemoryProvider struct {
	keys map[string][]byte // keyID -> root key
}

// NewMemoryProvider creates a new in-memory KMS provider for development.
func NewMemoryProvider() *MemoryProvider {
	return &MemoryProvider{
		keys: make(map[string][]byte),
	}
}

// Encrypt encrypts plaintext using the specified key ID with MAC authentication.
func (m *MemoryProvider) Encrypt(_ context.Context, keyID string, plaintext []byte) ([]byte, error) {
	key, exists := m.keys[keyID]
	if !exists {
		return nil, fmt.Errorf("key not found: %s", keyID)
	}
	// XOR encrypt
	ciphertext := make([]byte, len(plaintext))
	for i := range plaintext {
		ciphertext[i] = plaintext[i] ^ key[i%len(key)]
	}
	// Append MAC for integrity
	mac := hmac.New(sha256.New, key)
	mac.Write(ciphertext)
	macSum := mac.Sum(nil)
	return append(ciphertext, macSum...), nil
}

// Decrypt decrypts ciphertext using the specified key ID with MAC verification.
func (m *MemoryProvider) Decrypt(_ context.Context, keyID string, ciphertext []byte) ([]byte, error) {
	key, exists := m.keys[keyID]
	if !exists {
		return nil, fmt.Errorf("key not found: %s", keyID)
	}
	// Need at least 32 bytes for MAC
	if len(ciphertext) < 32 {
		return nil, fmt.Errorf("ciphertext too short")
	}
	// Split ciphertext and MAC
	data := ciphertext[:len(ciphertext)-32]
	macSum := ciphertext[len(ciphertext)-32:]
	// Verify MAC
	mac := hmac.New(sha256.New, key)
	mac.Write(data)
	expectedMAC := mac.Sum(nil)
	if !hmac.Equal(macSum, expectedMAC) {
		return nil, fmt.Errorf("MAC verification failed: wrong key or corrupted data")
	}
	// XOR decrypt
	plaintext := make([]byte, len(data))
	for i := range data {
		plaintext[i] = data[i] ^ key[i%len(key)]
	}
	return plaintext, nil
}

// CreateKey creates a new in-memory key.
func (m *MemoryProvider) CreateKey(_ context.Context, tenantID string) (string, error) {
	keyID := fmt.Sprintf("memory-%s-%d", tenantID, time.Now().UnixNano())
	// Generate a 32-byte random key for AES-256
	key := make([]byte, 32)
	if _, err := rand.Read(key); err != nil {
		return "", fmt.Errorf("failed to generate random key: %w", err)
	}
	m.keys[keyID] = key
	return keyID, nil
}

// DisableKey disables a key (no-op in memory provider).
func (m *MemoryProvider) DisableKey(_ context.Context, _ string) error {
	return nil
}

// ScheduleKeyDeletion schedules key deletion (removes from map in memory provider).
func (m *MemoryProvider) ScheduleKeyDeletion(_ context.Context, keyID string, windowDays int) (time.Time, error) {
	delete(m.keys, keyID)
	// Return deletion date (windowDays from now)
	return time.Now().Add(time.Duration(windowDays) * 24 * time.Hour), nil
}

// EnableKey re-enables a disabled key (no-op in memory provider).
func (m *MemoryProvider) EnableKey(_ context.Context, _ string) error {
	return nil
}

// HealthCheck always returns healthy for memory provider.
func (m *MemoryProvider) HealthCheck(_ context.Context) error {
	return nil
}

// Close releases resources (no-op for memory provider).
func (m *MemoryProvider) Close() error {
	return nil
}
