package providers

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
)

// ProviderConfig enthält die Konfiguration eines Providers
type ProviderConfig struct {
	Enabled  bool                   `json:"enabled"`
	Settings map[string]interface{} `json:"settings"`
}

// SignCSRResult enthält das Ergebnis einer CSR-Signierung
type SignCSRResult struct {
	CertificatePEM string `json:"certificatePEM"`
	OrderID        string `json:"orderId,omitempty"`
	Status         string `json:"status"`
	Message        string `json:"message,omitempty"`
}

// Provider Interface für alle SSL Certificate Provider
type Provider interface {
	// GetName gibt den Namen des Providers zurück
	GetName() string
	// GetDisplayName gibt den Anzeigenamen zurück
	GetDisplayName() string
	// GetDescription gibt eine Beschreibung zurück
	GetDescription() string
	// ValidateConfig validiert die Konfiguration
	ValidateConfig(settings map[string]interface{}) error
	// TestConnection testet die Verbindung zum Provider
	TestConnection(settings map[string]interface{}) error
	// GetRequiredSettings gibt die erforderlichen Einstellungen zurück
	GetRequiredSettings() []SettingField
	// SignCSR signiert einen CSR und gibt das Zertifikat zurück
	SignCSR(csrPEM string, settings map[string]interface{}) (*SignCSRResult, error)
	// GetCertificate ruft ein Zertifikat anhand der Zertifikat-ID ab
	GetCertificate(certificateID string, settings map[string]interface{}) (string, error)
}

// ProviderManager verwaltet alle Provider
type ProviderManager struct {
	providers map[string]Provider
	configs   map[string]*ProviderConfig
	configDir string
	mu        sync.RWMutex
}

var manager *ProviderManager
var once sync.Once

// GetManager gibt die Singleton-Instanz des ProviderManagers zurück
func GetManager() *ProviderManager {
	once.Do(func() {
		manager = &ProviderManager{
			providers: make(map[string]Provider),
			configs:   make(map[string]*ProviderConfig),
			configDir: "./config/providers",
		}
		manager.loadAllConfigs()
	})
	return manager
}

// RegisterProvider registriert einen neuen Provider
func (pm *ProviderManager) RegisterProvider(provider Provider) {
	pm.mu.Lock()
	defer pm.mu.Unlock()
	
	providerID := pm.getProviderID(provider.GetName())
	pm.providers[providerID] = provider
	
	// Lade Konfiguration falls vorhanden
	if pm.configs[providerID] == nil {
		pm.configs[providerID] = &ProviderConfig{
			Enabled:  false,
			Settings: make(map[string]interface{}),
		}
	}
}

// GetProvider gibt einen Provider zurück
func (pm *ProviderManager) GetProvider(id string) (Provider, bool) {
	pm.mu.RLock()
	defer pm.mu.RUnlock()
	provider, exists := pm.providers[id]
	return provider, exists
}

// GetAllProviders gibt alle registrierten Provider zurück
func (pm *ProviderManager) GetAllProviders() map[string]Provider {
	pm.mu.RLock()
	defer pm.mu.RUnlock()
	result := make(map[string]Provider)
	for id, provider := range pm.providers {
		result[id] = provider
	}
	return result
}

// GetProviderConfig gibt die Konfiguration eines Providers zurück
func (pm *ProviderManager) GetProviderConfig(id string) (*ProviderConfig, error) {
	pm.mu.RLock()
	defer pm.mu.RUnlock()
	
	config, exists := pm.configs[id]
	if !exists {
		return &ProviderConfig{
			Enabled:  false,
			Settings: make(map[string]interface{}),
		}, nil
	}
	return config, nil
}

// UpdateProviderConfig aktualisiert die Konfiguration eines Providers
func (pm *ProviderManager) UpdateProviderConfig(id string, config *ProviderConfig) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()
	
	provider, exists := pm.providers[id]
	if !exists {
		return fmt.Errorf("provider %s nicht gefunden", id)
	}
	
	// Validiere Konfiguration
	if err := provider.ValidateConfig(config.Settings); err != nil {
		return fmt.Errorf("ungültige Konfiguration: %v", err)
	}
	
	pm.configs[id] = config
	
	// Speichere Konfiguration in Datei
	return pm.saveConfig(id, config)
}

// SetProviderEnabled aktiviert/deaktiviert einen Provider
func (pm *ProviderManager) SetProviderEnabled(id string, enabled bool) error {
	pm.mu.Lock()
	defer pm.mu.Unlock()
	
	if pm.configs[id] == nil {
		pm.configs[id] = &ProviderConfig{
			Enabled:  enabled,
			Settings: make(map[string]interface{}),
		}
	} else {
		pm.configs[id].Enabled = enabled
	}
	
	return pm.saveConfig(id, pm.configs[id])
}

// getProviderID erstellt eine ID aus dem Provider-Namen
func (pm *ProviderManager) getProviderID(name string) string {
	return name
}

// loadAllConfigs lädt alle Konfigurationsdateien
func (pm *ProviderManager) loadAllConfigs() {
	// Stelle sicher, dass das Verzeichnis existiert
	os.MkdirAll(pm.configDir, 0755)
	
	// Lade alle JSON-Dateien im Konfigurationsverzeichnis
	files, err := filepath.Glob(filepath.Join(pm.configDir, "*.json"))
	if err != nil {
		return
	}
	
	for _, file := range files {
		id := filepath.Base(file[:len(file)-5]) // Entferne .json
		config, err := pm.loadConfig(id)
		if err == nil {
			pm.configs[id] = config
		}
	}
}

// loadConfig lädt eine Konfigurationsdatei
func (pm *ProviderManager) loadConfig(id string) (*ProviderConfig, error) {
	filePath := filepath.Join(pm.configDir, id+".json")
	
	data, err := os.ReadFile(filePath)
	if err != nil {
		return nil, err
	}
	
	var config ProviderConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, err
	}
	
	return &config, nil
}

// saveConfig speichert eine Konfiguration in eine Datei
func (pm *ProviderManager) saveConfig(id string, config *ProviderConfig) error {
	// Stelle sicher, dass das Verzeichnis existiert
	os.MkdirAll(pm.configDir, 0755)
	
	filePath := filepath.Join(pm.configDir, id+".json")
	
	data, err := json.MarshalIndent(config, "", "  ")
	if err != nil {
		return err
	}
	
	return os.WriteFile(filePath, data, 0644)
}

