// File Path: monorepo/cloud/maplepress-backend/internal/interface/http/handler/gateway/login_handler.go
package gateway

import (
	"errors"
	"fmt"
	"net/http"

	"go.uber.org/zap"

	gatewaydto "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/interface/http/dto/gateway"
	gatewaysvc "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/gateway"
	securityeventservice "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service/securityevent"
	gatewayuc "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/gateway"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httperror"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/httpresponse"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/ratelimit"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/clientip"
)

// LoginHandler handles HTTP requests for user login
type LoginHandler struct {
	loginService        gatewaysvc.LoginService
	loginRateLimiter    ratelimit.LoginRateLimiter
	securityEventLogger securityeventservice.Logger
	ipExtractor         *clientip.Extractor
	logger              *zap.Logger
}

// NewLoginHandler creates a new login handler
// CWE-307: Integrates rate limiting and account lockout protection
// CWE-778: Integrates security event logging for audit trails
func NewLoginHandler(
	loginService gatewaysvc.LoginService,
	loginRateLimiter ratelimit.LoginRateLimiter,
	securityEventLogger securityeventservice.Logger,
	ipExtractor *clientip.Extractor,
	logger *zap.Logger,
) *LoginHandler {
	return &LoginHandler{
		loginService:        loginService,
		loginRateLimiter:    loginRateLimiter,
		securityEventLogger: securityEventLogger,
		ipExtractor:         ipExtractor,
		logger:              logger.Named("login-handler"),
	}
}

// ProvideLoginHandler creates a new LoginHandler for dependency injection
func ProvideLoginHandler(
	loginService gatewaysvc.LoginService,
	loginRateLimiter ratelimit.LoginRateLimiter,
	securityEventLogger securityeventservice.Logger,
	ipExtractor *clientip.Extractor,
	logger *zap.Logger,
) *LoginHandler {
	return NewLoginHandler(loginService, loginRateLimiter, securityEventLogger, ipExtractor, logger)
}

// Handle processes POST /api/v1/login requests
// CWE-307: Implements rate limiting and account lockout protection against brute force attacks
func (h *LoginHandler) Handle(w http.ResponseWriter, r *http.Request) {
	h.logger.Debug("handling login request")

	// Parse and validate request
	dto, err := gatewaydto.ParseLoginRequest(r)
	if err != nil {
		h.logger.Warn("invalid login request", zap.Error(err))
		httperror.ProblemBadRequest(w, err.Error())
		return
	}

	// CWE-348: Extract client IP securely with trusted proxy validation
	clientIP := h.ipExtractor.Extract(r)

	// CWE-307: Check rate limits and account lockout BEFORE attempting authentication
	allowed, isLocked, remainingAttempts, err := h.loginRateLimiter.CheckAndRecordAttempt(
		r.Context(),
		dto.Email,
		clientIP,
	)
	if err != nil {
		// Log error but continue (fail open)
		h.logger.Error("rate limiter error",
			logger.EmailHash(dto.Email),
			zap.String("ip", clientIP),
			zap.Error(err))
	}

	// Account is locked - return error immediately
	if isLocked {
		h.logger.Warn("login attempt on locked account",
			logger.EmailHash(dto.Email),
			logger.SafeEmail("email_redacted", dto.Email),
			zap.String("ip", clientIP))

		// Add Retry-After header (30 minutes)
		w.Header().Set("Retry-After", "1800")

		httperror.ProblemTooManyRequests(w, "Account temporarily locked due to too many failed login attempts. Please try again later.")
		return
	}

	// IP rate limit exceeded - return error immediately
	if !allowed {
		h.logger.Warn("login rate limit exceeded",
			logger.EmailHash(dto.Email),
			zap.String("ip", clientIP))

		// CWE-778: Log security event for IP rate limit
		h.securityEventLogger.LogIPRateLimitExceeded(r.Context(), clientIP)

		// Add Retry-After header (15 minutes)
		w.Header().Set("Retry-After", "900")

		httperror.ProblemTooManyRequests(w, "Too many login attempts from this IP address. Please try again later.")
		return
	}

	// Execute login
	response, err := h.loginService.Login(r.Context(), &gatewaysvc.LoginInput{
		Email:    dto.Email,
		Password: dto.Password,
	})
	if err != nil {
		if errors.Is(err, gatewayuc.ErrInvalidCredentials) {
			// CWE-307: Record failed login attempt for account lockout tracking
			if err := h.loginRateLimiter.RecordFailedAttempt(r.Context(), dto.Email, clientIP); err != nil {
				h.logger.Error("failed to record failed login attempt",
					logger.EmailHash(dto.Email),
					zap.String("ip", clientIP),
					zap.Error(err))
			}

			// CWE-532: Log with redacted email (security event logging)
			h.logger.Warn("login failed: invalid credentials",
				logger.EmailHash(dto.Email),
				logger.SafeEmail("email_redacted", dto.Email),
				zap.String("ip", clientIP),
				zap.Int("remaining_attempts", remainingAttempts-1))

			// CWE-778: Log security event for failed login
			redactor := logger.NewSensitiveFieldRedactor()
			h.securityEventLogger.LogFailedLogin(r.Context(), redactor.HashForLogging(dto.Email), clientIP, remainingAttempts-1)

			// Include remaining attempts in error message to help legitimate users
			errorMsg := "Invalid email or password."
			if remainingAttempts <= 3 {
				errorMsg = fmt.Sprintf("Invalid email or password. %d attempts remaining before account lockout.", remainingAttempts-1)
			}

			httperror.ProblemUnauthorized(w, errorMsg)
			return
		}
		h.logger.Error("login failed", zap.Error(err))
		httperror.ProblemInternalServerError(w, "Failed to process login. Please try again later.")
		return
	}

	// CWE-307: Record successful login (resets failed attempt counters)
	if err := h.loginRateLimiter.RecordSuccessfulLogin(r.Context(), dto.Email, clientIP); err != nil {
		// Log error but don't fail the login
		h.logger.Error("failed to reset login counters after successful login",
			logger.EmailHash(dto.Email),
			zap.String("ip", clientIP),
			zap.Error(err))
	}

	// CWE-532: Log with safe identifiers only (no PII)
	h.logger.Info("login successful",
		zap.String("user_id", response.UserID),
		zap.String("tenant_id", response.TenantID),
		logger.EmailHash(response.UserEmail),
		zap.String("ip", clientIP))

	// CWE-778: Log security event for successful login
	redactor := logger.NewSensitiveFieldRedactor()
	h.securityEventLogger.LogSuccessfulLogin(r.Context(), redactor.HashForLogging(dto.Email), clientIP)

	// Return response with pretty JSON
	httpresponse.OK(w, response)
}
