package handlers

import (
	"context"
	"net/http"

	"veza-backend-api/internal/core/auth" // Added import for authcore
	"veza-backend-api/internal/services"

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

// RequestPasswordResetRequest represents a request to reset password
// T0193: Request structure for password reset endpoint
// MOD-P1-001: Ajout tags validate pour validation systématique
type RequestPasswordResetRequest struct {
	Email string `json:"email" binding:"required,email" validate:"required,email"`
}

// PasswordResetServiceInterface defines methods needed for password reset handler
type PasswordResetServiceInterface interface {
	GenerateToken() (string, error)
	StoreToken(userID uuid.UUID, token string) error
	VerifyToken(token string) (uuid.UUID, error)
	MarkTokenAsUsed(token string) error
	InvalidateOldTokens(userID uuid.UUID) error
}

// PasswordServiceInterface defines methods needed for password operations
type PasswordServiceInterface interface {
	GetUserByEmail(email string) (*services.UserInfo, error)
	ValidatePassword(password string) error
	UpdatePassword(userID uuid.UUID, password string) error
}

// EmailServiceInterface defines methods needed for email operations
type EmailServiceInterface interface {
	SendPasswordResetEmail(userID uuid.UUID, email, token string) error
}

// AuditServiceInterface defines methods needed for audit operations
type AuditServiceInterface interface {
	LogPasswordResetRequest(ctx context.Context, userID *uuid.UUID, email, ip, userAgent string) error
	LogPasswordReset(ctx context.Context, userID uuid.UUID, success bool, ip, userAgent string) error
}

// SessionServiceInterface defines methods needed for session operations
type SessionServiceInterface interface {
	RevokeAllUserSessions(ctx context.Context, userID uuid.UUID) (int64, error)
}

// AuthServiceInterface defines methods needed for auth operations
type AuthServiceInterface interface {
	InvalidateAllUserSessions(ctx context.Context, userID uuid.UUID, sessionService SessionServiceInterface) error
}

// RequestPasswordReset handles password reset request
// T0193: Creates endpoint POST /api/v1/auth/password/reset-request
// BE-SEC-013: Added audit logging for password reset requests
func RequestPasswordReset(
	passwordResetService *services.PasswordResetService,
	passwordService *services.PasswordService,
	emailService *services.EmailService,
	auditService *services.AuditService,
	logger *zap.Logger,
) gin.HandlerFunc {
	return RequestPasswordResetWithInterfaces(
		passwordResetService,
		passwordService,
		emailService,
		auditService,
		logger,
	)
}

// RequestPasswordResetWithInterfaces handles password reset request with interfaces (for testing)
func RequestPasswordResetWithInterfaces(
	passwordResetService PasswordResetServiceInterface,
	passwordService PasswordServiceInterface,
	emailService EmailServiceInterface,
	auditService AuditServiceInterface,
	logger *zap.Logger,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req RequestPasswordResetRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		// Find user by email
		user, err := passwordService.GetUserByEmail(req.Email)
		if err != nil {
			// Always return success for security (prevent email enumeration)
			RespondSuccess(c, http.StatusOK, gin.H{"message": "If the email exists, a reset link has been sent"})
			return
		}

		// Invalidate old tokens
		if err := passwordResetService.InvalidateOldTokens(user.ID); err != nil {
			logger.Error("Failed to invalidate old tokens",
				zap.String("user_id", user.ID.String()),
				zap.Error(err),
			)
			// Continue anyway, not critical
		}

		// Generate token
		token, err := passwordResetService.GenerateToken()
		if err != nil {
			logger.Error("Failed to generate password reset token",
				zap.String("user_id", user.ID.String()),
				zap.Error(err),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate token"})
			return
		}

		// Store token
		if err := passwordResetService.StoreToken(user.ID, token); err != nil {
			logger.Error("Failed to store password reset token",
				zap.String("user_id", user.ID.String()),
				zap.Error(err),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to store token"})
			return
		}

		// Send email
		if err := emailService.SendPasswordResetEmail(user.ID, user.Email, token); err != nil {
			// Log but don't fail - user should still get success message
			logger.Error("Failed to send password reset email",
				zap.String("user_id", user.ID.String()),
				zap.String("email", user.Email),
				zap.Error(err),
			)
		}

		// BE-SEC-013: Log password reset request
		if auditService != nil {
			userID := user.ID
			if err := auditService.LogPasswordResetRequest(c.Request.Context(), &userID, user.Email, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
				logger.Warn("Failed to log password reset request",
					zap.String("user_id", user.ID.String()),
					zap.Error(err),
				)
			}
		}

		// Always return generic success message for security
		RespondSuccess(c, http.StatusOK, gin.H{"message": "If the email exists, a reset link has been sent"})
	}
}

// ResetPasswordRequest represents a request to complete password reset
// T0194: Request structure for password reset completion
// MOD-P1-001: Ajout tags validate pour validation systématique
type ResetPasswordRequest struct {
	Token       string `json:"token" binding:"required" validate:"required"`
	NewPassword string `json:"new_password" binding:"required,min=8" validate:"required,min=8"`
}

// ResetPassword handles password reset completion
// T0194: Creates endpoint POST /api/v1/auth/password/reset
// T0200: Uses AuthService.InvalidateAllUserSessions to invalidate sessions and update token_version
// BE-SEC-013: Added audit logging for password reset completion
func ResetPassword(
	passwordResetService *services.PasswordResetService,
	passwordService *services.PasswordService,
	authService *auth.AuthService, // Changed to *auth.AuthService
	sessionService *services.SessionService,
	auditService *services.AuditService,
	logger *zap.Logger,
) gin.HandlerFunc {
	// Convert concrete types to interfaces
	var authServiceInterface AuthServiceInterface
	if authService != nil {
		authServiceInterface = &authServiceAdapter{authService: authService}
	}
	var sessionServiceInterface SessionServiceInterface
	if sessionService != nil {
		sessionServiceInterface = &sessionServiceAdapter{sessionService: sessionService}
	}
	return ResetPasswordWithInterfaces(
		passwordResetService,
		passwordService,
		authServiceInterface,
		sessionServiceInterface,
		auditService,
		logger,
	)
}

// authServiceAdapter adapts *auth.AuthService to AuthServiceInterface
type authServiceAdapter struct {
	authService *auth.AuthService
}

func (a *authServiceAdapter) InvalidateAllUserSessions(ctx context.Context, userID uuid.UUID, sessionService SessionServiceInterface) error {
	return a.authService.InvalidateAllUserSessions(ctx, userID, sessionService)
}

// sessionServiceAdapter adapts *services.SessionService to SessionServiceInterface
type sessionServiceAdapter struct {
	sessionService *services.SessionService
}

func (s *sessionServiceAdapter) RevokeAllUserSessions(ctx context.Context, userID uuid.UUID) (int64, error) {
	return s.sessionService.RevokeAllUserSessions(ctx, userID)
}

// ResetPasswordWithInterfaces handles password reset completion with interfaces (for testing)
func ResetPasswordWithInterfaces(
	passwordResetService PasswordResetServiceInterface,
	passwordService PasswordServiceInterface,
	authService AuthServiceInterface,
	sessionService SessionServiceInterface,
	auditService AuditServiceInterface,
	logger *zap.Logger,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		commonHandler := NewCommonHandler(logger)
		var req ResetPasswordRequest
		if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		// Verify token
		userID, err := passwordResetService.VerifyToken(req.Token)
		if err != nil {
			logger.Warn("Password reset token verification failed",
				zap.String("token", req.Token[:min(len(req.Token), 8)]+"..."),
				zap.Error(err),
			)
			// BE-SEC-013: Log failed password reset attempt
			if auditService != nil {
				if err := auditService.LogPasswordReset(c.Request.Context(), uuid.Nil, false, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
					logger.Warn("Failed to log password reset failure", zap.Error(err))
				}
			}
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid or expired token"})
			return
		}

		// Validate password strength
		if err := passwordService.ValidatePassword(req.NewPassword); err != nil {
			logger.Warn("Password validation failed",
				zap.String("user_id", userID.String()),
				zap.Error(err),
			)
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		// Update password
		if err := passwordService.UpdatePassword(userID, req.NewPassword); err != nil {
			logger.Error("Failed to update password",
				zap.String("user_id", userID.String()),
				zap.Error(err),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update password"})
			return
		}

		// Mark token as used
		if err := passwordResetService.MarkTokenAsUsed(req.Token); err != nil {
			// Log but don't fail - password is already updated
			logger.Warn("Failed to mark token as used",
				zap.String("user_id", userID.String()),
				zap.String("token", req.Token[:min(len(req.Token), 8)]+"..."),
				zap.Error(err),
			)
		}

		// T0200: Invalidate all user sessions via AuthService
		// This updates token_version and revokes all sessions
		if authService != nil && sessionService != nil {
			err := authService.InvalidateAllUserSessions(c.Request.Context(), userID, sessionService)
			if err != nil {
				// Log but don't fail - password is already updated
				logger.Warn("Failed to invalidate user sessions",
					zap.String("user_id", userID.String()),
					zap.Error(err),
				)
			} else {
				logger.Info("User sessions invalidated after password reset",
					zap.String("user_id", userID.String()),
				)
			}
		}

		logger.Info("Password reset completed successfully",
			zap.String("user_id", userID.String()),
		)

		// BE-SEC-013: Log successful password reset
		if auditService != nil {
			if err := auditService.LogPasswordReset(c.Request.Context(), userID, true, c.ClientIP(), c.GetHeader("User-Agent")); err != nil {
				logger.Warn("Failed to log password reset success",
					zap.String("user_id", userID.String()),
					zap.Error(err),
				)
			}
		}

		RespondSuccess(c, http.StatusOK, gin.H{"message": "Password reset successfully"})
	}
}

// min returns the minimum of two integers (helper function)
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
