package main

import (
	"log"
	"net/http"
	"strings"

	"golang.org/x/crypto/bcrypt"
	"sogoms.com/internal/admin"
)

// HandleTwoFAPage affiche la page de vérification 2FA.
func (s *AdminServer) HandleTwoFAPage(w http.ResponseWriter, r *http.Request) {
	// Récupérer la session (doit être pending)
	session, err := s.sessions.GetSessionFromRequest(r)
	if err != nil || session == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	// Vérifier que la session est en attente de 2FA
	if !session.TwoFAPending {
		http.Redirect(w, r, "/admin/", http.StatusSeeOther)
		return
	}

	data := map[string]any{
		"Title":     "Vérification 2FA",
		"CSRFToken": session.CSRFToken,
		"Error":     r.URL.Query().Get("error"),
		"Username":  session.Username,
	}

	s.render(w, "2fa_verify.html", data)
}

// HandleTwoFAVerify valide le code TOTP ou le code de secours.
func (s *AdminServer) HandleTwoFAVerify(w http.ResponseWriter, r *http.Request) {
	ip := getClientIP(r)

	// Rate limiting
	if !s.rateLimiter.Allow(ip) {
		http.Redirect(w, r, "/admin/2fa/verify?error=Trop+de+tentatives", http.StatusSeeOther)
		return
	}

	// Récupérer la session
	session, err := s.sessions.GetSessionFromRequest(r)
	if err != nil || session == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	if !session.TwoFAPending {
		http.Redirect(w, r, "/admin/", http.StatusSeeOther)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Redirect(w, r, "/admin/2fa/verify?error=Formulaire+invalide", http.StatusSeeOther)
		return
	}

	// Vérifier le CSRF token
	if r.FormValue("csrf_token") != session.CSRFToken {
		http.Redirect(w, r, "/admin/2fa/verify?error=Token+CSRF+invalide", http.StatusSeeOther)
		return
	}

	s.rateLimiter.Record(ip)

	// Récupérer l'utilisateur
	user := s.adminCfg.GetUser(session.Username)
	if user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	code := strings.TrimSpace(r.FormValue("code"))
	useBackup := r.FormValue("use_backup") == "true"
	backupCode := strings.TrimSpace(r.FormValue("backup_code"))

	var verified bool

	if useBackup && backupCode != "" {
		// Vérifier le code de secours
		index := VerifyBackupCode(backupCode, user.BackupCodes)
		if index >= 0 {
			verified = true
			// Supprimer le code utilisé
			user.BackupCodes = RemoveBackupCode(user.BackupCodes, index)
			// TODO: sauvegarder la config mise à jour
			log.Printf("[admin] 2FA backup code used by %s, %d remaining", session.Username, len(user.BackupCodes))
		}
	} else if code != "" {
		// Vérifier le code TOTP
		if ValidateTOTPCode(user.TwoFASecret, code) {
			verified = true
		}
	}

	if !verified {
		s.audit.LogAction(session.Username, "2fa_failed", "", map[string]any{
			"ip":         ip,
			"use_backup": useBackup,
		})
		http.Redirect(w, r, "/admin/2fa/verify?error=Code+invalide", http.StatusSeeOther)
		return
	}

	// 2FA validé - compléter la session
	s.sessions.CompleteTwoFA(session.ID)

	// Mettre à jour le cookie avec la nouvelle expiration
	session, _ = s.sessions.Get(session.ID)
	s.sessions.SetCookie(w, session)

	// Log succès
	s.audit.LogAction(session.Username, "2fa_verified", "", map[string]any{
		"ip":         ip,
		"use_backup": useBackup,
	})

	http.Redirect(w, r, "/admin/", http.StatusSeeOther)
}

// HandleTwoFASetupPage affiche la page de configuration 2FA.
func (s *AdminServer) HandleTwoFASetupPage(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	user := GetUserFromContext(r.Context())

	if session == nil || user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	// Si 2FA déjà activé, rediriger vers les paramètres
	if user.TwoFAEnabled {
		http.Redirect(w, r, "/admin/security?info=2FA+déjà+activé", http.StatusSeeOther)
		return
	}

	// Générer un nouveau secret TOTP
	key, err := GenerateTOTPSecret(s.adminCfg.TwoFA.IssuerName, user.Username)
	if err != nil {
		log.Printf("[admin] generate TOTP secret error: %v", err)
		http.Error(w, "Erreur génération secret", http.StatusInternalServerError)
		return
	}

	// Générer le QR code
	qrDataURL, err := GenerateQRCodeDataURL(key)
	if err != nil {
		log.Printf("[admin] generate QR code error: %v", err)
		http.Error(w, "Erreur génération QR code", http.StatusInternalServerError)
		return
	}

	// Générer les codes de secours
	backupCodes, err := GenerateBackupCodes(10)
	if err != nil {
		log.Printf("[admin] generate backup codes error: %v", err)
		http.Error(w, "Erreur génération codes secours", http.StatusInternalServerError)
		return
	}

	data := map[string]any{
		"Title":                "Activer 2FA",
		"User":                 user,
		"Session":              session,
		"CSRFToken":            session.CSRFToken,
		"QRCodeDataURL":        qrDataURL,
		"TwoFASecret":          key.Secret(),
		"BackupCodes":          backupCodes,
		"BackupCodesFormatted": FormatBackupCodes(backupCodes),
		"Error":                r.URL.Query().Get("error"),
	}

	s.render(w, "2fa_setup.html", data)
}

// HandleTwoFASetupConfirm confirme l'activation du 2FA.
func (s *AdminServer) HandleTwoFASetupConfirm(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	user := GetUserFromContext(r.Context())

	if session == nil || user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Redirect(w, r, "/admin/2fa/setup?error=Formulaire+invalide", http.StatusSeeOther)
		return
	}

	// Vérifier le CSRF token
	if r.FormValue("csrf_token") != session.CSRFToken {
		http.Redirect(w, r, "/admin/2fa/setup?error=Token+CSRF+invalide", http.StatusSeeOther)
		return
	}

	secret := r.FormValue("temp_secret")
	verifyCode := strings.TrimSpace(r.FormValue("verify_code"))
	backupCodesRaw := r.FormValue("backup_codes") // JSON array

	// Valider le code TOTP
	if !ValidateTOTPCode(secret, verifyCode) {
		http.Redirect(w, r, "/admin/2fa/setup?error=Code+invalide", http.StatusSeeOther)
		return
	}

	// Parser et hasher les backup codes
	backupCodes := strings.Split(backupCodesRaw, ",")
	hashedCodes, err := HashBackupCodes(backupCodes)
	if err != nil {
		log.Printf("[admin] hash backup codes error: %v", err)
		http.Error(w, "Erreur hash codes secours", http.StatusInternalServerError)
		return
	}

	// Mettre à jour l'utilisateur
	user.TwoFAEnabled = true
	user.TwoFASecret = secret
	user.BackupCodes = hashedCodes

	// Sauvegarder la configuration
	if err := s.saveAdminConfig(); err != nil {
		log.Printf("[admin] save admin config error: %v", err)
		http.Error(w, "Erreur sauvegarde configuration", http.StatusInternalServerError)
		return
	}

	// Log l'action
	s.audit.LogAction(user.Username, "2fa_enabled", "", map[string]any{
		"ip": getClientIP(r),
	})

	http.Redirect(w, r, "/admin/?flash=success&msg=2FA+activé+avec+succès", http.StatusSeeOther)
}

// HandleTwoFADisable désactive le 2FA.
func (s *AdminServer) HandleTwoFADisable(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	user := GetUserFromContext(r.Context())

	if session == nil || user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}

	// Vérifier le CSRF token
	if r.FormValue("csrf_token") != session.CSRFToken {
		http.Error(w, "Invalid CSRF token", http.StatusForbidden)
		return
	}

	// Vérifier si le rôle exige 2FA
	if user.NeedsTwoFA(&s.adminCfg.TwoFA) && !user.TwoFAEnabled {
		http.Error(w, "2FA obligatoire pour votre rôle", http.StatusForbidden)
		return
	}

	// Demander le mot de passe pour confirmer
	password := r.FormValue("password")
	if !verifyUserPassword(user, password) {
		http.Redirect(w, r, "/admin/security?error=Mot+de+passe+incorrect", http.StatusSeeOther)
		return
	}

	// Désactiver 2FA
	user.TwoFAEnabled = false
	user.TwoFASecret = ""
	user.BackupCodes = nil

	// Sauvegarder la configuration
	if err := s.saveAdminConfig(); err != nil {
		log.Printf("[admin] save admin config error: %v", err)
		http.Error(w, "Erreur sauvegarde configuration", http.StatusInternalServerError)
		return
	}

	// Log l'action
	s.audit.LogAction(user.Username, "2fa_disabled", "", map[string]any{
		"ip": getClientIP(r),
	})

	http.Redirect(w, r, "/admin/?flash=success&msg=2FA+désactivé", http.StatusSeeOther)
}

// HandleSecurityPage affiche la page de sécurité (2FA settings).
func (s *AdminServer) HandleSecurityPage(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	user := GetUserFromContext(r.Context())

	if session == nil || user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	// Vérifier si 2FA est requis pour ce user
	twoFARequired := user.NeedsTwoFA(&s.adminCfg.TwoFA)

	data := map[string]any{
		"Title":         "Sécurité",
		"User":          user,
		"Session":       session,
		"CSRFToken":     session.CSRFToken,
		"IsSuperAdmin":  user.IsSuperAdmin(),
		"TwoFAEnabled":  user.TwoFAEnabled,
		"TwoFARequired": twoFARequired,
		"BackupCount":   len(user.BackupCodes),
		"Error":         r.URL.Query().Get("error"),
		"Info":          r.URL.Query().Get("info"),
	}

	s.render(w, "security.html", data)
}

// saveAdminConfig sauvegarde la configuration admin dans le fichier YAML.
func (s *AdminServer) saveAdminConfig() error {
	return admin.SaveAdminConfig(s.adminCfg, "/secrets/admin_users.yaml")
}

// verifyUserPassword vérifie le mot de passe d'un utilisateur.
func verifyUserPassword(user *admin.AdminUser, password string) bool {
	if user == nil || password == "" {
		return false
	}
	// Utiliser bcrypt pour vérifier
	return checkPasswordHash(password, user.PasswordHash)
}

// checkPasswordHash vérifie un mot de passe contre son hash bcrypt.
func checkPasswordHash(password, hash string) bool {
	return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}

// HandleUsersPage affiche la liste des utilisateurs admin (super_admin only).
func (s *AdminServer) HandleUsersPage(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	user := GetUserFromContext(r.Context())

	if session == nil || user == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	// Seul super_admin peut voir cette page
	if !user.IsSuperAdmin() {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	// Construire la liste des utilisateurs
	type UserInfo struct {
		Username     string
		Email        string
		Role         string
		TwoFAEnabled bool
		BackupCount  int
	}

	users := make([]UserInfo, 0, len(s.adminCfg.Users))
	for _, u := range s.adminCfg.Users {
		users = append(users, UserInfo{
			Username:     u.Username,
			Email:        u.Email,
			Role:         u.Role,
			TwoFAEnabled: u.TwoFAEnabled,
			BackupCount:  len(u.BackupCodes),
		})
	}

	data := map[string]any{
		"Title":        "Utilisateurs",
		"User":         user,
		"Session":      session,
		"CSRFToken":    session.CSRFToken,
		"IsSuperAdmin": user.IsSuperAdmin(),
		"Users":        users,
		"Flash":        r.URL.Query().Get("flash"),
		"FlashMessage": r.URL.Query().Get("msg"),
	}

	s.render(w, "users.html", data)
}

// HandleReset2FA reset le 2FA d'un utilisateur (super_admin only).
func (s *AdminServer) HandleReset2FA(w http.ResponseWriter, r *http.Request) {
	session := GetSessionFromContext(r.Context())
	currentUser := GetUserFromContext(r.Context())

	if session == nil || currentUser == nil {
		http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
		return
	}

	// Seul super_admin peut reset le 2FA
	if !currentUser.IsSuperAdmin() {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}

	// Vérifier CSRF
	if r.FormValue("csrf_token") != session.CSRFToken {
		http.Error(w, "Invalid CSRF token", http.StatusForbidden)
		return
	}

	targetUsername := r.FormValue("username")
	if targetUsername == "" {
		http.Redirect(w, r, "/admin/users?flash=error&msg=Username+manquant", http.StatusSeeOther)
		return
	}

	// Trouver l'utilisateur cible
	targetUser := s.adminCfg.GetUser(targetUsername)
	if targetUser == nil {
		http.Redirect(w, r, "/admin/users?flash=error&msg=Utilisateur+non+trouvé", http.StatusSeeOther)
		return
	}

	// Reset le 2FA
	targetUser.TwoFAEnabled = false
	targetUser.TwoFASecret = ""
	targetUser.BackupCodes = nil

	// Sauvegarder
	if err := s.saveAdminConfig(); err != nil {
		log.Printf("[admin] save admin config error: %v", err)
		http.Redirect(w, r, "/admin/users?flash=error&msg=Erreur+sauvegarde", http.StatusSeeOther)
		return
	}

	// Log l'action
	s.audit.LogAction(currentUser.Username, "2fa_reset", targetUsername, map[string]any{
		"ip":          getClientIP(r),
		"target_user": targetUsername,
	})

	log.Printf("[admin] 2FA reset for user %s by %s", targetUsername, currentUser.Username)

	http.Redirect(w, r, "/admin/users?flash=success&msg=2FA+réinitialisé+pour+"+targetUsername, http.StatusSeeOther)
}
