package handlers

import (
	"github.com/google/uuid"
	"net/http"
	"strconv"
	"strings"
	"time"

	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

// SessionHandler gère les opérations sur les sessions
type SessionHandler struct {
	sessionService *services.SessionService
	auditService   *services.AuditService
	logger         *zap.Logger
}

// NewSessionHandler crée un nouveau handler de session
func NewSessionHandler(
	sessionService *services.SessionService,
	auditService *services.AuditService,
	logger *zap.Logger,
) *SessionHandler {
	return &SessionHandler{
		sessionService: sessionService,
		auditService:   auditService,
		logger:         logger,
	}
}

// Logout gère la déconnexion d'un utilisateur
func (sh *SessionHandler) Logout() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

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

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

		tokenString := tokenParts[1]

		// Révoquer la session
		err := sh.sessionService.RevokeSession(c.Request.Context(), tokenString)
		if err != nil {
			sh.logger.Error("Failed to revoke session",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to logout"})
			return
		}

		// Log la déconnexion
		// Temporarily disabled due to type mismatch (int64 vs uuid.UUID)
		/*
		err = sh.auditService.LogLogout(
			c.Request.Context(),
			userID,
			c.ClientIP(),
			c.GetHeader("User-Agent"),
		)
		if err != nil {
			sh.logger.Error("Failed to log logout",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
		}
		*/

		sh.logger.Info("User logged out",
			zap.String("user_id", strconv.FormatInt(userID, 10)),
			zap.String("ip", c.ClientIP()),
		)

		c.JSON(http.StatusOK, gin.H{
			"message": "Logged out successfully",
		})
	}
}

// LogoutAll gère la déconnexion de toutes les sessions d'un utilisateur
func (sh *SessionHandler) LogoutAll() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

		// Révoquer toutes les sessions
		revokedCount, err := sh.sessionService.RevokeAllUserSessions(c.Request.Context(), userID)
		if err != nil {
			sh.logger.Error("Failed to revoke all user sessions",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to logout all sessions"})
			return
		}

		// Log la déconnexion globale
		// Temporarily disabled due to type mismatch
		/*
		err = sh.auditService.LogAction(c.Request.Context(), &services.AuditLogCreateRequest{
			UserID:    &userID,
			Action:    "logout_all_sessions",
			Resource:  "user",
			IPAddress: c.ClientIP(),
			UserAgent: c.GetHeader("User-Agent"),
			Metadata: map[string]interface{}{
				"sessions_revoked": revokedCount,
			},
		})
		if err != nil {
			sh.logger.Error("Failed to log logout all sessions",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
		}
		*/

		sh.logger.Info("All user sessions revoked",
			zap.String("user_id", strconv.FormatInt(userID, 10)),
			zap.Int64("sessions_revoked", revokedCount),
			zap.String("ip", c.ClientIP()),
		)

		c.JSON(http.StatusOK, gin.H{
			"message":          "All sessions logged out successfully",
			"sessions_revoked": revokedCount,
		})
	}
}

// GetSessions récupère toutes les sessions actives d'un utilisateur
func (sh *SessionHandler) GetSessions() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

		// Récupérer les sessions
		sessions, err := sh.sessionService.GetUserSessions(userID)
		if err != nil {
			sh.logger.Error("Failed to get user sessions",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get sessions"})
			return
		}

		// Formater les sessions pour la réponse
		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,
				"is_current": false, // TODO: Déterminer si c'est la session actuelle
			}
			sessionList = append(sessionList, sessionData)
		}

		c.JSON(http.StatusOK, gin.H{
			"sessions": sessionList,
			"count":    len(sessionList),
		})
	}
}

// RevokeSession révoque une session spécifique
func (sh *SessionHandler) RevokeSession() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

		// Récupérer l'ID de session depuis les paramètres (INT64 now)
		sessionIDStr := c.Param("session_id")
		sessionID, err := strconv.ParseInt(sessionIDStr, 10, 64)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid session ID"})
			return
		}

		// Récupérer les sessions de l'utilisateur pour vérifier la propriété
		sessions, err := sh.sessionService.GetUserSessions(userID)
		if err != nil {
			sh.logger.Error("Failed to get user sessions",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get sessions"})
			return
		}

		// Vérifier que la session appartient à l'utilisateur
		sessionFound := false
		for _, session := range sessions {
			if session.ID == sessionID {
				sessionFound = true
				break
			}
		}

		if !sessionFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Session not found"})
			return
		}

		// Révoquer la session par ID
		// TODO: Implémenter RevokeSessionByID dans SessionService
		// Pour l'instant, on ne peut pas révoquer par ID sans token hash
		// Mais Session struct a TokenHash!
		// We found the session, so we have the token hash.
		// Wait, we need to iterate to find the session object.
		var targetSession *services.Session
		for _, session := range sessions {
			if session.ID == sessionID {
				targetSession = session
				break
			}
		}
		
		if targetSession != nil {
			// Revoke by Hash using DeleteSession
			err = sh.sessionService.DeleteSession(targetSession.TokenHash)
			if err != nil {
				sh.logger.Error("Failed to revoke session",
					zap.Error(err),
					zap.String("user_id", strconv.FormatInt(userID, 10)),
				)
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to revoke session"})
				return
			}
		}

		// Log la révocation
		/*
		err = sh.auditService.LogAction(c.Request.Context(), &services.AuditLogCreateRequest{
			UserID:     &userID,
			Action:     "revoke_session",
			Resource:   "session",
			ResourceID: &sessionID, // Mismatch type
			IPAddress:  c.ClientIP(),
			UserAgent:  c.GetHeader("User-Agent"),
			Metadata: map[string]interface{}{
				"session_id": strconv.FormatInt(sessionID, 10),
			},
		})
		if err != nil {
			sh.logger.Error("Failed to log session revocation",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
		}
		*/

		sh.logger.Info("Session revoked",
			zap.String("user_id", strconv.FormatInt(userID, 10)),
			zap.String("session_id", strconv.FormatInt(sessionID, 10)),
			zap.String("ip", c.ClientIP()),
		)

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

// GetSessionStats récupère les statistiques des sessions
func (sh *SessionHandler) GetSessionStats() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

		// Récupérer les statistiques
		stats, err := sh.sessionService.GetSessionStats(c.Request.Context())
		if err != nil {
			sh.logger.Error("Failed to get session stats",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get session stats"})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"user_id": userID,
			"stats":   stats,
		})
	}
}

// RefreshSession rafraîchit une session
func (sh *SessionHandler) RefreshSession() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte
		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
		}

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

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

		tokenString := tokenParts[1]

		// Rafraîchir la session
		newExpiresIn := 24 * time.Hour // 24 heures
		err := sh.sessionService.RefreshSession(c.Request.Context(), tokenString, newExpiresIn)
		if err != nil {
			sh.logger.Error("Failed to refresh session",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to refresh session"})
			return
		}

		// Log le rafraîchissement
		/*
		err = sh.auditService.LogAction(c.Request.Context(), &services.AuditLogCreateRequest{
			UserID:    &userID,
			Action:    "session_refresh",
			Resource:  "session",
			IPAddress: c.ClientIP(),
			UserAgent: c.GetHeader("User-Agent"),
			Metadata: map[string]interface{}{
				"new_expires_in": newExpiresIn.String(),
			},
		})
		if err != nil {
			sh.logger.Error("Failed to log session refresh",
				zap.Error(err),
				zap.String("user_id", strconv.FormatInt(userID, 10)),
			)
		}
		*/

		sh.logger.Info("Session refreshed",
			zap.String("user_id", strconv.FormatInt(userID, 10)),
			zap.String("ip", c.ClientIP()),
		)

		c.JSON(http.StatusOK, gin.H{
			"message":    "Session refreshed successfully",
			"expires_in": newExpiresIn.Seconds(),
			"expires_at": time.Now().Add(newExpiresIn),
		})
	}
}
