package handlers

import (
	"errors"
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog/log"

	"github.com/treytartt/honeydue-api/internal/apperrors"
	"github.com/treytartt/honeydue-api/internal/dto/requests"
	"github.com/treytartt/honeydue-api/internal/dto/responses"
	"github.com/treytartt/honeydue-api/internal/middleware"
	"github.com/treytartt/honeydue-api/internal/services"
	"github.com/treytartt/honeydue-api/internal/validator"
)

// AuthHandler handles authentication endpoints
type AuthHandler struct {
	authService       *services.AuthService
	emailService      *services.EmailService
	cache             *services.CacheService
	appleAuthService  *services.AppleAuthService
	googleAuthService *services.GoogleAuthService
	storageService    *services.StorageService
	auditService      *services.AuditService
}

// NewAuthHandler creates a new auth handler
func NewAuthHandler(authService *services.AuthService, emailService *services.EmailService, cache *services.CacheService) *AuthHandler {
	return &AuthHandler{
		authService:  authService,
		emailService: emailService,
		cache:        cache,
	}
}

// SetAppleAuthService sets the Apple auth service (called after initialization)
func (h *AuthHandler) SetAppleAuthService(appleAuth *services.AppleAuthService) {
	h.appleAuthService = appleAuth
}

// SetGoogleAuthService sets the Google auth service (called after initialization)
func (h *AuthHandler) SetGoogleAuthService(googleAuth *services.GoogleAuthService) {
	h.googleAuthService = googleAuth
}

// SetStorageService sets the storage service for file deletion during account deletion
func (h *AuthHandler) SetStorageService(storageService *services.StorageService) {
	h.storageService = storageService
}

// SetAuditService sets the audit service for logging security events
func (h *AuthHandler) SetAuditService(auditService *services.AuditService) {
	h.auditService = auditService
}

// Login handles POST /api/auth/login/
func (h *AuthHandler) Login(c echo.Context) error {
	var req requests.LoginRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	response, err := h.authService.Login(&req)
	if err != nil {
		log.Debug().Err(err).Str("identifier", req.Username).Msg("Login failed")
		if h.auditService != nil {
			h.auditService.LogEvent(c, nil, services.AuditEventLoginFailed, map[string]interface{}{
				"identifier": req.Username,
			})
		}
		return err
	}

	if h.auditService != nil {
		userID := response.User.ID
		h.auditService.LogEvent(c, &userID, services.AuditEventLogin, nil)
	}

	return c.JSON(http.StatusOK, response)
}

// Register handles POST /api/auth/register/
func (h *AuthHandler) Register(c echo.Context) error {
	var req requests.RegisterRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	response, confirmationCode, err := h.authService.Register(&req)
	if err != nil {
		log.Debug().Err(err).Msg("Registration failed")
		return err
	}

	if h.auditService != nil {
		userID := response.User.ID
		h.auditService.LogEvent(c, &userID, services.AuditEventRegister, map[string]interface{}{
			"username": req.Username,
			"email":    req.Email,
		})
	}

	// Send welcome email with confirmation code (async)
	if h.emailService != nil && confirmationCode != "" {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", req.Email).Msg("Panic in welcome email goroutine")
				}
			}()
			if err := h.emailService.SendWelcomeEmail(req.Email, req.FirstName, confirmationCode); err != nil {
				log.Error().Err(err).Str("email", req.Email).Msg("Failed to send welcome email")
			}
		}()
	}

	return c.JSON(http.StatusCreated, response)
}

// Logout handles POST /api/auth/logout/
func (h *AuthHandler) Logout(c echo.Context) error {
	token := middleware.GetAuthToken(c)
	if token == "" {
		return apperrors.Unauthorized("error.not_authenticated")
	}

	// Log audit event before invalidating the token
	if h.auditService != nil {
		user := middleware.GetAuthUser(c)
		if user != nil {
			h.auditService.LogEvent(c, &user.ID, services.AuditEventLogout, nil)
		}
	}

	// Invalidate token in database
	if err := h.authService.Logout(token); err != nil {
		log.Warn().Err(err).Msg("Failed to delete token from database")
	}

	// Invalidate token in cache
	if h.cache != nil {
		if err := h.cache.InvalidateAuthToken(c.Request().Context(), token); err != nil {
			log.Warn().Err(err).Msg("Failed to invalidate token in cache")
		}
	}

	return c.JSON(http.StatusOK, responses.MessageResponse{Message: "Logged out successfully"})
}

// CurrentUser handles GET /api/auth/me/
func (h *AuthHandler) CurrentUser(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	response, err := h.authService.GetCurrentUser(user.ID)
	if err != nil {
		log.Error().Err(err).Uint("user_id", user.ID).Msg("Failed to get current user")
		return err
	}

	return c.JSON(http.StatusOK, response)
}

// UpdateProfile handles PUT/PATCH /api/auth/profile/
func (h *AuthHandler) UpdateProfile(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	var req requests.UpdateProfileRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	response, err := h.authService.UpdateProfile(user.ID, &req)
	if err != nil {
		log.Debug().Err(err).Uint("user_id", user.ID).Msg("Failed to update profile")
		return err
	}

	return c.JSON(http.StatusOK, response)
}

// VerifyEmail handles POST /api/auth/verify-email/
func (h *AuthHandler) VerifyEmail(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	var req requests.VerifyEmailRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	err = h.authService.VerifyEmail(user.ID, req.Code)
	if err != nil {
		log.Debug().Err(err).Uint("user_id", user.ID).Msg("Email verification failed")
		return err
	}

	// Send post-verification welcome email with tips (async)
	if h.emailService != nil {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", user.Email).Msg("Panic in post-verification email goroutine")
				}
			}()
			if err := h.emailService.SendPostVerificationEmail(user.Email, user.FirstName); err != nil {
				log.Error().Err(err).Str("email", user.Email).Msg("Failed to send post-verification email")
			}
		}()
	}

	return c.JSON(http.StatusOK, responses.VerifyEmailResponse{
		Message:  "Email verified successfully",
		Verified: true,
	})
}

// ResendVerification handles POST /api/auth/resend-verification/
func (h *AuthHandler) ResendVerification(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	code, err := h.authService.ResendVerificationCode(user.ID)
	if err != nil {
		log.Debug().Err(err).Uint("user_id", user.ID).Msg("Failed to resend verification")
		return err
	}

	// Send verification email (async)
	if h.emailService != nil {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", user.Email).Msg("Panic in verification email goroutine")
				}
			}()
			if err := h.emailService.SendVerificationEmail(user.Email, user.FirstName, code); err != nil {
				log.Error().Err(err).Str("email", user.Email).Msg("Failed to send verification email")
			}
		}()
	}

	return c.JSON(http.StatusOK, responses.MessageResponse{Message: "Verification email sent"})
}

// ForgotPassword handles POST /api/auth/forgot-password/
func (h *AuthHandler) ForgotPassword(c echo.Context) error {
	var req requests.ForgotPasswordRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	code, user, err := h.authService.ForgotPassword(req.Email)
	if err != nil {
		var appErr *apperrors.AppError
		if errors.As(err, &appErr) && appErr.Code == http.StatusTooManyRequests {
			// Only reveal rate limit errors
			return err
		}

		log.Error().Err(err).Str("email", req.Email).Msg("Forgot password failed")
		// Don't reveal other errors to prevent email enumeration
	}

	// Send password reset email (async) - only if user found
	if h.emailService != nil && code != "" && user != nil {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", user.Email).Msg("Panic in password reset email goroutine")
				}
			}()
			if err := h.emailService.SendPasswordResetEmail(user.Email, user.FirstName, code); err != nil {
				log.Error().Err(err).Str("email", user.Email).Msg("Failed to send password reset email")
			}
		}()
	}

	if h.auditService != nil {
		h.auditService.LogEvent(c, nil, services.AuditEventPasswordReset, map[string]interface{}{
			"email": req.Email,
		})
	}

	// Always return success to prevent email enumeration
	return c.JSON(http.StatusOK, responses.ForgotPasswordResponse{
		Message: "Password reset email sent",
	})
}

// VerifyResetCode handles POST /api/auth/verify-reset-code/
func (h *AuthHandler) VerifyResetCode(c echo.Context) error {
	var req requests.VerifyResetCodeRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	resetToken, err := h.authService.VerifyResetCode(req.Email, req.Code)
	if err != nil {
		log.Debug().Err(err).Str("email", req.Email).Msg("Verify reset code failed")
		return err
	}

	return c.JSON(http.StatusOK, responses.VerifyResetCodeResponse{
		Message:    "Reset code verified",
		ResetToken: resetToken,
	})
}

// ResetPassword handles POST /api/auth/reset-password/
func (h *AuthHandler) ResetPassword(c echo.Context) error {
	var req requests.ResetPasswordRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	err := h.authService.ResetPassword(req.ResetToken, req.NewPassword)
	if err != nil {
		log.Debug().Err(err).Msg("Password reset failed")
		return err
	}

	if h.auditService != nil {
		h.auditService.LogEvent(c, nil, services.AuditEventPasswordChanged, map[string]interface{}{
			"method": "reset_token",
		})
	}

	return c.JSON(http.StatusOK, responses.ResetPasswordResponse{
		Message: "Password reset successful",
	})
}

// AppleSignIn handles POST /api/auth/apple-sign-in/
func (h *AuthHandler) AppleSignIn(c echo.Context) error {
	var req requests.AppleSignInRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	if h.appleAuthService == nil {
		log.Error().Msg("Apple auth service not configured")
		return &apperrors.AppError{
			Code:       500,
			MessageKey: "error.apple_signin_not_configured",
		}
	}

	response, err := h.authService.AppleSignIn(c.Request().Context(), h.appleAuthService, &req)
	if err != nil {
		// Check for legacy Apple Sign In error (not yet migrated)
		if errors.Is(err, services.ErrAppleSignInFailed) {
			log.Debug().Err(err).Msg("Apple Sign In failed (legacy error)")
			return apperrors.Unauthorized("error.invalid_apple_token")
		}

		log.Debug().Err(err).Msg("Apple Sign In failed")
		return err
	}

	// Send welcome email for new users (async)
	if response.IsNewUser && h.emailService != nil && response.User.Email != "" {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", response.User.Email).Msg("Panic in Apple welcome email goroutine")
				}
			}()
			if err := h.emailService.SendAppleWelcomeEmail(response.User.Email, response.User.FirstName); err != nil {
				log.Error().Err(err).Str("email", response.User.Email).Msg("Failed to send Apple welcome email")
			}
		}()
	}

	return c.JSON(http.StatusOK, response)
}

// GoogleSignIn handles POST /api/auth/google-sign-in/
func (h *AuthHandler) GoogleSignIn(c echo.Context) error {
	var req requests.GoogleSignInRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}
	if err := c.Validate(&req); err != nil {
		return c.JSON(http.StatusBadRequest, validator.FormatValidationErrors(err))
	}

	if h.googleAuthService == nil {
		log.Error().Msg("Google auth service not configured")
		return &apperrors.AppError{
			Code:       500,
			MessageKey: "error.google_signin_not_configured",
		}
	}

	response, err := h.authService.GoogleSignIn(c.Request().Context(), h.googleAuthService, &req)
	if err != nil {
		// Check for legacy Google Sign In error (not yet migrated)
		if errors.Is(err, services.ErrGoogleSignInFailed) {
			log.Debug().Err(err).Msg("Google Sign In failed (legacy error)")
			return apperrors.Unauthorized("error.invalid_google_token")
		}

		log.Debug().Err(err).Msg("Google Sign In failed")
		return err
	}

	// Send welcome email for new users (async)
	if response.IsNewUser && h.emailService != nil && response.User.Email != "" {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Str("email", response.User.Email).Msg("Panic in Google welcome email goroutine")
				}
			}()
			if err := h.emailService.SendGoogleWelcomeEmail(response.User.Email, response.User.FirstName); err != nil {
				log.Error().Err(err).Str("email", response.User.Email).Msg("Failed to send Google welcome email")
			}
		}()
	}

	return c.JSON(http.StatusOK, response)
}

// RefreshToken handles POST /api/auth/refresh/
func (h *AuthHandler) RefreshToken(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	token := middleware.GetAuthToken(c)
	if token == "" {
		return apperrors.Unauthorized("error.not_authenticated")
	}

	response, err := h.authService.RefreshToken(token, user.ID)
	if err != nil {
		log.Debug().Err(err).Uint("user_id", user.ID).Msg("Token refresh failed")
		return err
	}

	// If the token was refreshed (new token), invalidate the old one from cache
	if response.Token != token && h.cache != nil {
		if cacheErr := h.cache.InvalidateAuthToken(c.Request().Context(), token); cacheErr != nil {
			log.Warn().Err(cacheErr).Msg("Failed to invalidate old token from cache during refresh")
		}
	}

	return c.JSON(http.StatusOK, response)
}

// DeleteAccount handles DELETE /api/auth/account/
func (h *AuthHandler) DeleteAccount(c echo.Context) error {
	user, err := middleware.MustGetAuthUser(c)
	if err != nil {
		return err
	}

	var req requests.DeleteAccountRequest
	if err := c.Bind(&req); err != nil {
		return apperrors.BadRequest("error.invalid_request")
	}

	fileURLs, err := h.authService.DeleteAccount(user.ID, req.Password, req.Confirmation)
	if err != nil {
		log.Debug().Err(err).Uint("user_id", user.ID).Msg("Account deletion failed")
		return err
	}

	if h.auditService != nil {
		h.auditService.LogEvent(c, &user.ID, services.AuditEventAccountDeleted, map[string]interface{}{
			"user_id":  user.ID,
			"username": user.Username,
			"email":    user.Email,
		})
	}

	// Delete files from disk (best effort, don't fail the request)
	if h.storageService != nil && len(fileURLs) > 0 {
		go func() {
			defer func() {
				if r := recover(); r != nil {
					log.Error().Interface("panic", r).Uint("user_id", user.ID).Msg("Panic in file cleanup goroutine")
				}
			}()
			for _, fileURL := range fileURLs {
				if err := h.storageService.Delete(fileURL); err != nil {
					log.Warn().Err(err).Str("file_url", fileURL).Msg("Failed to delete file during account cleanup")
				}
			}
		}()
	}

	// Invalidate auth token from cache
	token := middleware.GetAuthToken(c)
	if h.cache != nil && token != "" {
		if err := h.cache.InvalidateAuthToken(c.Request().Context(), token); err != nil {
			log.Warn().Err(err).Msg("Failed to invalidate token in cache after account deletion")
		}
	}

	return c.JSON(http.StatusOK, responses.MessageResponse{Message: "Account deleted successfully"})
}
