package auth

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

	"veza-backend-api/internal/dto"
	"veza-backend-api/internal/response"
	"veza-backend-api/internal/services"

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

// AuthHandler gère les requêtes d'authentification pour T0151
type AuthHandler struct {
	authService    *AuthService // Changed to *AuthService (from the current package)
	sessionService *services.SessionService
	logger         *zap.Logger
}

// NewAuthHandler crée une nouvelle instance d'AuthHandler
func NewAuthHandler(authService *AuthService, sessionService *services.SessionService, logger *zap.Logger) *AuthHandler { // Changed to *AuthService
	return &AuthHandler{
		authService:    authService,
		sessionService: sessionService,
		logger:         logger,
	}
}

// Register gère l'inscription d'un nouvel utilisateur
func (h *AuthHandler) Register(c *gin.Context) {
	var req dto.RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		errorMsg := err.Error()
		if strings.Contains(errorMsg, "Password") && strings.Contains(errorMsg, "min") {
			errorMsg = "Le mot de passe doit contenir au moins 12 caractères"
		} else if strings.Contains(errorMsg, "PasswordConfirm") && strings.Contains(errorMsg, "eqfield") {
			errorMsg = "Les mots de passe ne correspondent pas"
		} else if strings.Contains(errorMsg, "Email") && strings.Contains(errorMsg, "email") {
			errorMsg = "Format d'email invalide"
		} else if strings.Contains(errorMsg, "required") {
			if strings.Contains(errorMsg, "Password") {
				errorMsg = "Le mot de passe est requis"
			} else if strings.Contains(errorMsg, "Email") {
				errorMsg = "L'email est requis"
			} else if strings.Contains(errorMsg, "PasswordConfirm") {
				errorMsg = "La confirmation du mot de passe est requise"
			}
		}

		h.logger.Warn("Invalid registration request", zap.Error(err), zap.String("error_message", errorMsg))
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, errorMsg)
		return
	}

	h.logger.Info("Received registration request", zap.Any("req", req))
	user, tokens, err := h.authService.Register(c.Request.Context(), req.Email, req.Username, req.Password)
	if err != nil {
		if strings.Contains(err.Error(), "already exists") {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H
			response.Error(c, http.StatusConflict, err.Error())
			return
		}
		if strings.Contains(err.Error(), "validation") || strings.Contains(err.Error(), "invalid") {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H
			response.Error(c, http.StatusBadRequest, err.Error())
			return
		}
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusInternalServerError, "Failed to create user")
		return
	}

	// Construire la réponse avec les tokens générés
	response := dto.RegisterResponse{
		User: dto.UserResponse{
			ID:       user.ID,
			Email:    user.Email,
			Username: user.Username,
		},
		Token: dto.TokenResponse{
			AccessToken:  tokens.AccessToken,
			RefreshToken: tokens.RefreshToken,
			ExpiresIn:    tokens.ExpiresIn,
		},
	}

	c.JSON(http.StatusCreated, response)
}

// Login gère la connexion d'un utilisateur
func (h *AuthHandler) Login(c *gin.Context) {
	var req dto.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	user, tokens, err := h.authService.Login(c.Request.Context(), req.Email, req.Password, req.RememberMe)
	if err != nil {
		if strings.Contains(err.Error(), "email not verified") {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H (403 -> ErrCodeForbidden)
			response.Error(c, http.StatusForbidden, err.Error())
			return
		}
		if strings.Contains(err.Error(), "invalid credentials") {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H
			response.Error(c, http.StatusUnauthorized, "Invalid credentials")
			return
		}
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusInternalServerError, "Failed to authenticate")
		return
	}

	if h.sessionService != nil {
		ipAddress := c.ClientIP()
		userAgent := c.GetHeader("User-Agent")
		if userAgent == "" {
			userAgent = "Unknown"
		}

		expiresIn := 30 * 24 * time.Hour
		if req.RememberMe {
			expiresIn = 90 * 24 * time.Hour
		}

		sessionReq := &services.SessionCreateRequest{
			UserID:    user.ID,
			Token:     tokens.AccessToken,
			IPAddress: ipAddress,
			UserAgent: userAgent,
			ExpiresIn: expiresIn,
		}

		if _, err := h.sessionService.CreateSession(c.Request.Context(), sessionReq); err != nil {
			h.logger.Warn("Failed to create session after login",
				zap.String("user_id", user.ID.String()),
				zap.String("ip_address", ipAddress),
				zap.Error(err),
			)
		}
	}

	response := dto.LoginResponse{
		User: dto.UserResponse{
			ID:    user.ID,
			Email: user.Email,
		},
		Token: dto.TokenResponse{
			AccessToken:  tokens.AccessToken,
			RefreshToken: tokens.RefreshToken,
			ExpiresIn:    int(h.authService.JWTService.GetConfig().AccessTokenTTL.Seconds()),
		},
	}

	c.JSON(http.StatusOK, response)
}

// Refresh gère le rafraîchissement d'un access token
func (h *AuthHandler) Refresh(c *gin.Context) {
	var req dto.RefreshRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	tokens, err := h.authService.Refresh(c.Request.Context(), req.RefreshToken)
	if err != nil {
		if strings.Contains(err.Error(), "invalid refresh token") ||
			strings.Contains(err.Error(), "not found") ||
			strings.Contains(err.Error(), "expired") ||
			strings.Contains(err.Error(), "token version mismatch") {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H
			response.Error(c, http.StatusUnauthorized, "Invalid refresh token")
			return
		}
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusInternalServerError, "Failed to refresh token")
		return
	}

	response := dto.TokenResponse{
		AccessToken:  tokens.AccessToken,
		RefreshToken: tokens.RefreshToken,
		ExpiresIn:    900,
	}

	c.JSON(http.StatusOK, response)
}

// CheckUsername vérifie la disponibilité d'un nom d'utilisateur
func (h *AuthHandler) CheckUsername(c *gin.Context) {
	username := c.Query("username")
	if username == "" {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, "Username is required")
		return
	}

	_, err := h.authService.GetUserByUsername(c.Request.Context(), username)
	available := err != nil

	c.JSON(http.StatusOK, gin.H{
		"available": available,
		"username":  username,
	})
}

// GetMe retourne les informations de l'utilisateur connecté
func (h *AuthHandler) GetMe(c *gin.Context) {
	userID, exists := c.Get("user_id")
	if !exists {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusUnauthorized, "Unauthorized")
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"id":    userID,
		"email": c.GetString("email"),
		"role":  c.GetString("role"),
	})
}

// Logout déconnecte l'utilisateur
func (h *AuthHandler) Logout(c *gin.Context) {
	userIDInterface, exists := c.Get("user_id")
	if !exists {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusUnauthorized, "Unauthorized")
		return
	}

	userID, ok := userIDInterface.(uuid.UUID)
	if !ok {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusInternalServerError, "Invalid user ID type in context")
		return
	}

	var req struct {
		RefreshToken string `json:"refresh_token" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, "Refresh token is required")
		return
	}

	if err := h.authService.Logout(c.Request.Context(), userID, req.RefreshToken); err != nil {
		h.logger.Error("Failed to logout (revoke token)", zap.Error(err))
	}

	if h.sessionService != nil {
		authHeader := c.GetHeader("Authorization")
		if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
			token := strings.TrimPrefix(authHeader, "Bearer ")
			if err := h.sessionService.RevokeSession(c.Request.Context(), token); err != nil {
				h.logger.Warn("Failed to revoke session on logout", zap.Error(err))
			}
		}
	}

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

// VerifyEmail gère la vérification de l'email
func (h *AuthHandler) VerifyEmail(c *gin.Context) {
	token := c.Query("token")
	if token == "" {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, "Token required")
		return
	}

	if err := h.authService.VerifyEmail(c.Request.Context(), token); err != nil {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

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

// ResendVerification gère la demande de renvoi d'email de vérification
func (h *AuthHandler) ResendVerification(c *gin.Context) {
	var req struct {
		Email string `json:"email" binding:"required,email"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		// MOD-P2-003: Utiliser AppError au lieu de gin.H
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}

	if err := h.authService.ResendVerificationEmail(c.Request.Context(), req.Email); err != nil {
		if err.Error() == "email already verified" {
			// MOD-P2-003: Utiliser AppError au lieu de gin.H
			response.Error(c, http.StatusBadRequest, err.Error())
			return
		}
	}

	c.JSON(http.StatusOK, gin.H{"message": "Verification email sent if account exists"})
}

// GetUserByUsername gets a user by username
func (h *AuthHandler) GetUserByUsername(c *gin.Context) {
	username := c.Param("username")
	user, err := h.authService.GetUserByUsername(c.Request.Context(), username)
	if err != nil {
		response.NotFound(c, "User not found")
		return
	}
	response.Success(c, user)
}
