package handlers

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

	"veza-backend-api/internal/core/auth"
	"veza-backend-api/internal/dto"
	apperrors "veza-backend-api/internal/errors"
	// "veza-backend-api/internal/response" // Removed this import
	"veza-backend-api/internal/services"

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

// Login gère la connexion des utilisateurs
// T0203: Intègre création de session après login avec IP et User-Agent
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
func Login(authService *auth.AuthService, sessionService *services.SessionService, logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req dto.LoginRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		// req.RememberMe is a bool, not *bool, so no need to check for nil or indirect
		rememberMe := req.RememberMe 
		
		user, tokens, err := authService.Login(c.Request.Context(), req.Email, req.Password, rememberMe)
		if err != nil {
			if strings.Contains(err.Error(), "email not verified") {
				c.JSON(http.StatusForbidden, gin.H{
					"error": err.Error(),
					"code":  "EMAIL_NOT_VERIFIED",
				})
				return
			}
			if strings.Contains(err.Error(), "invalid credentials") {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
				return
			}
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to authenticate"})
			return
		}

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

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

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

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

		c.JSON(http.StatusOK, dto.LoginResponse{
			User: dto.UserResponse{
				ID:    user.ID,
				Email: user.Email,
			},
			Token: dto.TokenResponse{
				AccessToken:  tokens.AccessToken,
				RefreshToken: tokens.RefreshToken,
				ExpiresIn:    int(authService.JWTService.Config.AccessTokenTTL.Seconds()),
			},
		})
	}
}

// Register gère l'inscription des utilisateurs
// GO-013: Utilise validator centralisé pour validation améliorée
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
func Register(authService *auth.AuthService, logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req dto.RegisterRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		user, err := authService.Register(c.Request.Context(), req.Email, req.Password)
		if err != nil {
			switch {
			case services.IsUserAlreadyExistsError(err):
				c.JSON(http.StatusConflict, gin.H{"error": "User already exists"})
			case services.IsInvalidEmail(err):
				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email format"})
			case services.IsWeakPassword(err):
				c.JSON(http.StatusBadRequest, gin.H{"error": "Password does not meet requirements"})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"})
			}
			return
		}

		c.JSON(http.StatusCreated, dto.RegisterResponse{
			User: dto.UserResponse{
				ID:       user.ID,
				Email:    user.Email,
				Username: user.Username,
			},
		})
	}
}

// Refresh gère le rafraîchissement d'un access token
// GO-013: Utilise validator centralisé pour validation améliorée
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
func Refresh(authService *auth.AuthService, logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req dto.RefreshRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		tokens, err := 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") {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid refresh token"})
				return
			}
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to refresh token"})
			return
		}

		c.JSON(http.StatusOK, dto.TokenResponse{
			AccessToken:  tokens.AccessToken,
			RefreshToken: tokens.RefreshToken,
			ExpiresIn:    int(authService.JWTService.Config.AccessTokenTTL.Seconds()), // Use JWT config
		})
	}
}

// Logout gère la déconnexion des utilisateurs
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
func Logout(authService *auth.AuthService, sessionService *services.SessionService, logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type in context"))
			return
		}

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

		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		if err := authService.Logout(c.Request.Context(), userID, req.RefreshToken); err != nil {
			// Log the error but don't fail the request to prevent leaking info
		}

		if sessionService != nil {
			authHeader := c.GetHeader("Authorization")
			if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
				token := strings.TrimPrefix(authHeader, "Bearer ")
				if err := sessionService.RevokeSession(c.Request.Context(), token); err != nil {
					// Log the error but don't fail the request
				}
			}
		}

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

// VerifyEmail gère la vérification de l'email
func VerifyEmail(authService *auth.AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.Query("token")
		if token == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Token required"})
			return
		}

		if err := authService.VerifyEmail(c.Request.Context(), token); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": 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
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
func ResendVerification(authService *auth.AuthService, logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req dto.ResendVerificationRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		if err := authService.ResendVerificationEmail(c.Request.Context(), req.Email); err != nil {
			if strings.Contains(err.Error(), "email already verified") {
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}
		}

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

// CheckUsername vérifie la disponibilité d'un nom d'utilisateur
func CheckUsername(authService *auth.AuthService) gin.HandlerFunc {
	return func(c *gin.Context) {
		username := c.Query("username")
		if username == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Username is required"})
			return
		}

		_, err := 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 GetMe() gin.HandlerFunc {
	return func(c *gin.Context) {
		userID, exists := c.Get("user_id")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

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