package handlers

import (
	"net/http"

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

	"github.com/RyanCopley/skybridge/user/internal/domain"
	"github.com/RyanCopley/skybridge/user/internal/services"
)

// AuthHandler handles HTTP requests for authentication operations
type AuthHandler struct {
	authService services.AuthService
	userService services.UserService
	logger      *zap.Logger
}

// NewAuthHandler creates a new authentication handler
func NewAuthHandler(authService services.AuthService, userService services.UserService, logger *zap.Logger) *AuthHandler {
	return &AuthHandler{
		authService: authService,
		userService: userService,
		logger:      logger,
	}
}

// Register handles POST /auth/register
func (h *AuthHandler) Register(c *gin.Context) {
	var req domain.RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid registration request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	response, err := h.authService.Register(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Registration failed", zap.String("email", req.Email), zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Registration failed",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusCreated, response)
}

// Login handles POST /auth/login
func (h *AuthHandler) Login(c *gin.Context) {
	var req domain.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid login request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	response, err := h.authService.Login(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Login failed", zap.String("email", req.Email), zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "Authentication failed",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// Logout handles POST /auth/logout
func (h *AuthHandler) Logout(c *gin.Context) {
	token := getTokenFromHeader(c)
	if token == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Authorization header required",
		})
		return
	}

	err := h.authService.Logout(c.Request.Context(), token)
	if err != nil {
		h.logger.Error("Logout failed", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Logout failed",
			"details": err.Error(),
		})
		return
	}

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

// RefreshToken handles POST /auth/refresh
func (h *AuthHandler) RefreshToken(c *gin.Context) {
	token := getTokenFromHeader(c)
	if token == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Authorization header required",
		})
		return
	}

	response, err := h.authService.RefreshToken(c.Request.Context(), token)
	if err != nil {
		h.logger.Error("Token refresh failed", zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "Token refresh failed",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// ForgotPassword handles POST /auth/forgot-password
func (h *AuthHandler) ForgotPassword(c *gin.Context) {
	var req domain.ForgotPasswordRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid forgot password request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	err := h.authService.ForgotPassword(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Forgot password failed", zap.String("email", req.Email), zap.Error(err))
		// Don't reveal whether email exists for security
		c.JSON(http.StatusOK, gin.H{
			"message": "If the email exists, password reset instructions have been sent",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Password reset instructions have been sent to your email",
	})
}

// ResetPassword handles POST /auth/reset-password
func (h *AuthHandler) ResetPassword(c *gin.Context) {
	var req domain.ResetPasswordRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid reset password request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	err := h.authService.ResetPassword(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Password reset failed", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Password reset failed",
			"details": err.Error(),
		})
		return
	}

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

// ChangePassword handles POST /auth/change-password
func (h *AuthHandler) ChangePassword(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	var req domain.ChangePasswordRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid change password request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	actorID := getActorFromContext(c)
	err := h.authService.ChangePassword(c.Request.Context(), userID, &req, actorID)
	if err != nil {
		h.logger.Error("Change password failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Change password failed",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Password has been successfully changed",
	})
}

// VerifyEmail handles POST /auth/verify-email
func (h *AuthHandler) VerifyEmail(c *gin.Context) {
	var req domain.VerifyEmailRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid email verification request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	err := h.authService.VerifyEmail(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Email verification failed", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Email verification failed",
			"details": err.Error(),
		})
		return
	}

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

// ResendVerification handles POST /auth/resend-verification
func (h *AuthHandler) ResendVerification(c *gin.Context) {
	var req domain.ResendVerificationRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid resend verification request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	err := h.authService.ResendVerification(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("Resend verification failed", zap.String("email", req.Email), zap.Error(err))
		// Don't reveal whether email exists
		c.JSON(http.StatusOK, gin.H{
			"message": "If the email exists and is not verified, verification instructions have been sent",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Verification email has been sent",
	})
}

// Two-Factor Authentication endpoints

// SetupTwoFactor handles GET /auth/2fa/setup
func (h *AuthHandler) SetupTwoFactor(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	response, err := h.authService.SetupTwoFactor(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Setup 2FA failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Failed to setup two-factor authentication",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// EnableTwoFactor handles POST /auth/2fa/enable
func (h *AuthHandler) EnableTwoFactor(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	var req domain.EnableTwoFactorRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid enable 2FA request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	actorID := getActorFromContext(c)
	err := h.authService.EnableTwoFactor(c.Request.Context(), userID, &req, actorID)
	if err != nil {
		h.logger.Error("Enable 2FA failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Failed to enable two-factor authentication",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Two-factor authentication has been enabled",
	})
}

// DisableTwoFactor handles POST /auth/2fa/disable
func (h *AuthHandler) DisableTwoFactor(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	var req domain.DisableTwoFactorRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid disable 2FA request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	actorID := getActorFromContext(c)
	err := h.authService.DisableTwoFactor(c.Request.Context(), userID, &req, actorID)
	if err != nil {
		h.logger.Error("Disable 2FA failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Failed to disable two-factor authentication",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Two-factor authentication has been disabled",
	})
}

// ValidateTwoFactor handles POST /auth/2fa/validate
func (h *AuthHandler) ValidateTwoFactor(c *gin.Context) {
	var req domain.ValidateTwoFactorRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid 2FA validation request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error":   "Invalid request body",
			"details": err.Error(),
		})
		return
	}

	response, err := h.authService.ValidateTwoFactor(c.Request.Context(), &req, getIPAddress(c), c.GetHeader("User-Agent"))
	if err != nil {
		h.logger.Error("2FA validation failed", zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{
			"error":   "Two-factor authentication validation failed",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// RegenerateTwoFactorBackupCodes handles POST /auth/2fa/regenerate-codes
func (h *AuthHandler) RegenerateTwoFactorBackupCodes(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	actorID := getActorFromContext(c)
	codes, err := h.authService.RegenerateTwoFactorBackupCodes(c.Request.Context(), userID, actorID)
	if err != nil {
		h.logger.Error("Regenerate backup codes failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Failed to regenerate backup codes",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"backup_codes": codes,
	})
}

// Session Management endpoints

// GetSessions handles GET /auth/sessions
func (h *AuthHandler) GetSessions(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	response, err := h.authService.GetUserSessions(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Get sessions failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Failed to get sessions",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, response)
}

// RevokeSession handles DELETE /auth/sessions/:sessionId
func (h *AuthHandler) RevokeSession(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	sessionIDParam := c.Param("sessionId")
	sessionID, err := uuid.Parse(sessionIDParam)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Invalid session ID",
		})
		return
	}

	actorID := getActorFromContext(c)
	err = h.authService.RevokeSession(c.Request.Context(), userID, sessionID, actorID)
	if err != nil {
		h.logger.Error("Revoke session failed", zap.String("user_id", userID.String()), zap.String("session_id", sessionID.String()), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Failed to revoke session",
			"details": err.Error(),
		})
		return
	}

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

// RevokeAllSessions handles DELETE /auth/sessions
func (h *AuthHandler) RevokeAllSessions(c *gin.Context) {
	userID := getUserIDFromContext(c)
	if userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Authentication required",
		})
		return
	}

	actorID := getActorFromContext(c)
	err := h.authService.RevokeAllSessions(c.Request.Context(), userID, actorID)
	if err != nil {
		h.logger.Error("Revoke all sessions failed", zap.String("user_id", userID.String()), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":   "Failed to revoke all sessions",
			"details": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "All sessions have been revoked",
	})
}

// Helper functions

func getTokenFromHeader(c *gin.Context) string {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		return ""
	}
	
	// Remove "Bearer " prefix
	if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
		return authHeader[7:]
	}
	
	return authHeader
}

func getIPAddress(c *gin.Context) string {
	// Check for forwarded IP addresses
	if forwarded := c.GetHeader("X-Forwarded-For"); forwarded != "" {
		return forwarded
	}
	if realIP := c.GetHeader("X-Real-IP"); realIP != "" {
		return realIP
	}
	return c.ClientIP()
}

func getUserIDFromContext(c *gin.Context) uuid.UUID {
	if userID, exists := c.Get("user_id"); exists {
		if id, ok := userID.(uuid.UUID); ok {
			return id
		}
		if idStr, ok := userID.(string); ok {
			if id, err := uuid.Parse(idStr); err == nil {
				return id
			}
		}
	}
	return uuid.Nil
}