package handlers

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
)

// GetActiveSessions récupère la liste des sessions actives de l'utilisateur
// T0205: Endpoint pour récupérer les sessions actives avec metadata
func GetActiveSessions(sessionService *services.SessionService) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer user_id depuis context (middleware)
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		var userID int64
		switch v := userIDInterface.(type) {
		case int64:
			userID = v
		case float64:
			userID = int64(v)
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Appeler SessionService.GetUserSessions
		sessions, err := sessionService.GetUserSessions(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get sessions"})
			return
		}

		// Formater les sessions pour la réponse avec metadata
		var sessionList []map[string]interface{}
		for _, session := range sessions {
			sessionData := map[string]interface{}{
				"id":         session.ID,
				"created_at": session.CreatedAt,
				"expires_at": session.ExpiresAt,
				"ip_address": session.IPAddress,
				"user_agent": session.UserAgent,
			}
			// Ajouter is_current si c'est la session actuelle
			currentSessionID, exists := c.Get("session_id")
			if exists {
				if currentSessionID.(int64) == session.ID {
					sessionData["is_current"] = true
				} else {
					sessionData["is_current"] = false
				}
			} else {
				sessionData["is_current"] = false
			}
			sessionList = append(sessionList, sessionData)
		}

		// Retourner liste sessions avec metadata
		c.JSON(http.StatusOK, gin.H{
			"sessions": sessionList,
			"count":    len(sessionList),
		})
	}
}

// RevokeSession révoque une session spécifique (T0206)
// DELETE /api/v1/auth/sessions/:sessionId
func RevokeSession(sessionService *services.SessionService, tokenBlacklist *services.TokenBlacklist) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer user_id depuis context (middleware)
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(int64)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Extraire session_id depuis URL parameter
		sessionIDStr := c.Param("sessionId")
		sessionID, err := strconv.ParseInt(sessionIDStr, 10, 64)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid session ID"})
			return
		}

		// Récupérer la session pour vérifier la propriété
		session, err := sessionService.GetSessionByID(sessionID)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "session not found"})
			return
		}

		// Vérifier que la session appartient à l'utilisateur
		if session.UserID != userID {
			c.JSON(http.StatusForbidden, gin.H{"error": "unauthorized"})
			return
		}

		// Supprimer la session
		if err := sessionService.DeleteSession(session.TokenHash); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to revoke session"})
			return
		}

		// Ajouter le token à la blacklist
		// Calculer le TTL restant jusqu'à l'expiration
		ttl := time.Until(session.ExpiresAt)
		if ttl > 0 {
			if err := tokenBlacklist.AddTokenHash(c.Request.Context(), session.TokenHash, ttl); err != nil {
				// Log l'erreur mais ne pas faire échouer la requête
				// La session est déjà supprimée, c'est l'important
			}
		}

		c.JSON(http.StatusOK, gin.H{"message": "session revoked"})
	}
}

// RevokeAllSessions révoque toutes les sessions utilisateur sauf la session actuelle (T0207)
// DELETE /api/v1/auth/sessions
func RevokeAllSessions(sessionService *services.SessionService, tokenBlacklist *services.TokenBlacklist) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer user_id depuis context (middleware)
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
			return
		}

		userID, ok := userIDInterface.(int64)
		if !ok {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"})
			return
		}

		// Extraire le token actuel depuis le header Authorization
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Authorization header required"})
			return
		}

		// Vérifier le format Bearer token
		parts := strings.Split(authHeader, " ")
		if len(parts) != 2 || parts[0] != "Bearer" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Authorization header format"})
			return
		}

		currentToken := parts[1]
		currentTokenHash := sessionService.HashTokenForMiddleware(currentToken)

		// Récupérer toutes les sessions de l'utilisateur
		sessions, err := sessionService.GetUserSessions(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get sessions"})
			return
		}

		// Révoquer toutes les sessions sauf la session actuelle
		revokedCount := 0
		for _, session := range sessions {
			if session.TokenHash != currentTokenHash {
				// Supprimer la session
				if err := sessionService.DeleteSession(session.TokenHash); err != nil {
					// Log l'erreur mais continuer avec les autres sessions
					continue
				}

				// Ajouter le token à la blacklist
				ttl := time.Until(session.ExpiresAt)
				if ttl > 0 && tokenBlacklist != nil {
					_ = tokenBlacklist.AddTokenHash(c.Request.Context(), session.TokenHash, ttl)
				}

				revokedCount++
			}
		}

		c.JSON(http.StatusOK, gin.H{
			"message":        "all other sessions revoked",
			"revoked_count":  revokedCount,
			"total_sessions": len(sessions),
		})
	}
}

