// Package admin gère la configuration et les permissions de l'interface d'administration.
package admin

import (
	"fmt"
	"os"
	"strings"

	"gopkg.in/yaml.v3"
)

// AdminConfig représente la configuration complète de l'admin.
type AdminConfig struct {
	Session   SessionConfig   `yaml:"session"`
	RateLimit RateLimitConfig `yaml:"rate_limit"`
	TwoFA     TwoFAConfig     `yaml:"two_fa"`
	Users     []AdminUser     `yaml:"users"`
}

// TwoFAConfig configure l'authentification à deux facteurs.
type TwoFAConfig struct {
	Enabled       bool     `yaml:"enabled"`
	IssuerName    string   `yaml:"issuer_name"`
	RequiredRoles []string `yaml:"required_roles"` // rôles obligés d'avoir 2FA
}

// SessionConfig configure les sessions.
type SessionConfig struct {
	SecretFile string `yaml:"secret_file"`
	MaxAge     int    `yaml:"max_age"`      // secondes
	CookieName string `yaml:"cookie_name"`
	Secret     string `yaml:"-"` // chargé depuis fichier
}

// RateLimitConfig configure le rate limiting.
type RateLimitConfig struct {
	LoginMax    int `yaml:"login_max"`
	LoginWindow int `yaml:"login_window"` // secondes
}

// AdminUser représente un utilisateur admin.
type AdminUser struct {
	Username     string   `yaml:"username"`
	PasswordHash string   `yaml:"password_hash"`
	Role         string   `yaml:"role"`
	Email        string   `yaml:"email"`
	Apps         []string `yaml:"apps,omitempty"`        // pour app_admin
	Permissions  []string `yaml:"permissions,omitempty"` // pour app_admin
	// 2FA
	TwoFAEnabled bool     `yaml:"two_fa_enabled,omitempty"`
	TwoFASecret  string   `yaml:"two_fa_secret,omitempty"`  // base32 encoded
	BackupCodes  []string `yaml:"backup_codes,omitempty"`   // bcrypt hashed
}

// IsSuperAdmin retourne true si l'utilisateur est super_admin.
func (u *AdminUser) IsSuperAdmin() bool {
	return u.Role == "super_admin"
}

// NeedsTwoFA retourne true si l'utilisateur doit utiliser 2FA.
func (u *AdminUser) NeedsTwoFA(cfg *TwoFAConfig) bool {
	if !cfg.Enabled {
		return false
	}
	// Si 2FA activé pour cet utilisateur
	if u.TwoFAEnabled {
		return true
	}
	// Si le rôle est dans la liste des rôles obligés
	for _, role := range cfg.RequiredRoles {
		if u.Role == role {
			return true
		}
	}
	return false
}

// LoadAdminConfig charge la configuration admin depuis un fichier YAML.
func LoadAdminConfig(path string) (*AdminConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("read admin config: %w", err)
	}

	var cfg AdminConfig
	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("parse admin config: %w", err)
	}

	// Valeurs par défaut
	if cfg.Session.MaxAge == 0 {
		cfg.Session.MaxAge = 3600 // 1 heure
	}
	if cfg.Session.CookieName == "" {
		cfg.Session.CookieName = "sogoms_admin_sid"
	}
	if cfg.RateLimit.LoginMax == 0 {
		cfg.RateLimit.LoginMax = 5
	}
	if cfg.RateLimit.LoginWindow == 0 {
		cfg.RateLimit.LoginWindow = 60
	}
	if cfg.TwoFA.IssuerName == "" {
		cfg.TwoFA.IssuerName = "SOGOMS Admin"
	}

	// Charger le secret de session depuis le fichier
	if cfg.Session.SecretFile != "" {
		secretData, err := os.ReadFile(cfg.Session.SecretFile)
		if err != nil {
			return nil, fmt.Errorf("read session secret: %w", err)
		}
		cfg.Session.Secret = strings.TrimSpace(string(secretData))
	}

	// Valider
	if len(cfg.Users) == 0 {
		return nil, fmt.Errorf("no users defined")
	}
	if cfg.Session.Secret == "" {
		return nil, fmt.Errorf("session secret is required")
	}

	return &cfg, nil
}

// GetUser retourne un utilisateur par son username.
func (cfg *AdminConfig) GetUser(username string) *AdminUser {
	for i := range cfg.Users {
		if cfg.Users[i].Username == username {
			return &cfg.Users[i]
		}
	}
	return nil
}

// GetUserByEmail retourne un utilisateur par son email.
func (cfg *AdminConfig) GetUserByEmail(email string) *AdminUser {
	for i := range cfg.Users {
		if cfg.Users[i].Email == email {
			return &cfg.Users[i]
		}
	}
	return nil
}

// SaveAdminConfig sauvegarde la configuration admin dans un fichier YAML.
func SaveAdminConfig(cfg *AdminConfig, path string) error {
	// Créer une copie sans le secret en mémoire pour la sauvegarde
	saveCfg := *cfg
	saveCfg.Session.Secret = "" // Ne pas sauvegarder le secret déchiffré

	data, err := yaml.Marshal(&saveCfg)
	if err != nil {
		return fmt.Errorf("marshal admin config: %w", err)
	}

	if err := os.WriteFile(path, data, 0600); err != nil {
		return fmt.Errorf("write admin config: %w", err)
	}

	return nil
}
