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

import (
	"encoding/json"
	"net/http"
	"strings"

	"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"
	"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/httpvalidation"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/clientip"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/validation"
)

// RegisterHandler handles user registration HTTP requests
type RegisterHandler struct {
	service     gatewaysvc.RegisterService
	ipExtractor *clientip.Extractor
	logger      *zap.Logger
}

// ProvideRegisterHandler creates a new RegisterHandler
func ProvideRegisterHandler(
	service gatewaysvc.RegisterService,
	ipExtractor *clientip.Extractor,
	logger *zap.Logger,
) *RegisterHandler {
	return &RegisterHandler{
		service:     service,
		ipExtractor: ipExtractor,
		logger:      logger,
	}
}

// Handle handles the HTTP request for user registration
func (h *RegisterHandler) Handle(w http.ResponseWriter, r *http.Request) {
	// CWE-436: Validate Content-Type before parsing to prevent interpretation conflicts
	if err := httpvalidation.RequireJSONContentType(r); err != nil {
		h.logger.Warn("invalid content type",
			zap.String("content_type", r.Header.Get("Content-Type")))
		httperror.ProblemBadRequest(w, err.Error())
		return
	}

	// Parse request body
	var req gatewaydto.RegisterRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		h.logger.Warn("invalid request body", zap.Error(err))
		httperror.ProblemBadRequest(w, "Invalid request body format. Please check your JSON syntax.")
		return
	}

	// CWE-20: Comprehensive input validation
	if err := req.Validate(); err != nil {
		h.logger.Warn("registration request validation failed", zap.Error(err))

		// Check if it's a structured validation error (RFC 9457 format)
		if validationErr, ok := err.(*gatewaydto.ValidationErrors); ok {
			httperror.ValidationError(w, validationErr.Errors, "One or more validation errors occurred")
			return
		}

		// Fallback for non-structured errors
		httperror.ProblemBadRequest(w, err.Error())
		return
	}

	// CWE-348: Extract IP address securely with X-Forwarded-For validation
	// Only trusts X-Forwarded-For if request comes from configured trusted proxies
	ipAddress := h.ipExtractor.Extract(r)

	// Default timezone to UTC if not provided
	timezone := req.Timezone
	if timezone == "" {
		timezone = "UTC"
		h.logger.Debug("timezone not provided, defaulting to UTC")
	}

	// Generate tenant slug from tenant name
	validator := validation.NewValidator()
	tenantSlug := validator.GenerateSlug(req.TenantName)
	h.logger.Debug("generated tenant slug from name",
		zap.String("tenant_name", req.TenantName),
		zap.String("tenant_slug", tenantSlug))

	// Map DTO to service input
	input := &gatewaysvc.RegisterInput{
		Email:      req.Email,
		Password:   req.Password,
		FirstName:  req.FirstName,
		LastName:   req.LastName,
		TenantName: req.TenantName,
		TenantSlug: tenantSlug,
		Timezone:   timezone,

		// Consent fields
		AgreeTermsOfService: req.AgreeTermsOfService,
		AgreePromotions:     req.AgreePromotions,
		AgreeToTrackingAcrossThirdPartyAppsAndServices: req.AgreeToTrackingAcrossThirdPartyAppsAndServices,

		// IP address for audit trail
		CreatedFromIPAddress: ipAddress,
	}

	// Call service
	output, err := h.service.Register(r.Context(), input)
	if err != nil {
		// CWE-532: Log with redacted sensitive information
		h.logger.Error("failed to register user",
			zap.Error(err),
			logger.EmailHash(req.Email),
			logger.SafeEmail("email_redacted", req.Email),
			logger.TenantSlugHash(tenantSlug),
			logger.SafeTenantSlug("tenant_slug_redacted", tenantSlug))

		// Check for specific errors
		errMsg := err.Error()
		switch {
		case errMsg == "user already exists":
			// CWE-203: Return generic message to prevent user enumeration
			httperror.ProblemConflict(w, "Registration failed. The provided information is already in use.")
		case errMsg == "tenant already exists":
			// CWE-203: Return generic message to prevent tenant slug enumeration
			// Prevents attackers from discovering valid tenant slugs for reconnaissance
			httperror.ProblemConflict(w, "Registration failed. The provided information is already in use.")
		case errMsg == "must agree to terms of service":
			httperror.ProblemBadRequest(w, "You must agree to the terms of service to create an account.")
		case errMsg == "password must be at least 8 characters":
			httperror.ProblemBadRequest(w, "Password must be at least 8 characters long.")
		// CWE-521: Password breach checking
		case strings.Contains(errMsg, "data breaches"):
			httperror.ProblemBadRequest(w, "This password has been found in data breaches and cannot be used. Please choose a different password.")
		// CWE-521: Granular password strength errors for better user experience
		case errMsg == "password must contain at least one uppercase letter (A-Z)":
			httperror.ProblemBadRequest(w, "Password must contain at least one uppercase letter (A-Z).")
		case errMsg == "password must contain at least one lowercase letter (a-z)":
			httperror.ProblemBadRequest(w, "Password must contain at least one lowercase letter (a-z).")
		case errMsg == "password must contain at least one number (0-9)":
			httperror.ProblemBadRequest(w, "Password must contain at least one number (0-9).")
		case errMsg == "password must contain at least one special character (!@#$%^&*()_+-=[]{}; etc.)":
			httperror.ProblemBadRequest(w, "Password must contain at least one special character (!@#$%^&*()_+-=[]{}; etc.).")
		case errMsg == "password must contain uppercase, lowercase, number, and special character":
			httperror.ProblemBadRequest(w, "Password must contain uppercase, lowercase, number, and special character.")
		case errMsg == "invalid email format":
			httperror.ProblemBadRequest(w, "Invalid email format. Please provide a valid email address.")
		case errMsg == "tenant slug must contain only lowercase letters, numbers, and hyphens":
			httperror.ProblemBadRequest(w, "Tenant name must contain only lowercase letters, numbers, and hyphens.")
		default:
			httperror.ProblemInternalServerError(w, "Failed to register user. Please try again later.")
		}
		return
	}

	// CWE-532: Log with safe identifiers (no PII)
	h.logger.Info("user registered successfully",
		zap.String("user_id", output.UserID),
		zap.String("tenant_id", output.TenantID),
		logger.EmailHash(output.UserEmail))

	// Map to response DTO
	response := gatewaydto.RegisterResponse{
		UserID:        output.UserID,
		UserEmail:     output.UserEmail,
		UserName:      output.UserName,
		UserRole:      output.UserRole,
		TenantID:      output.TenantID,
		TenantName:    output.TenantName,
		TenantSlug:    output.TenantSlug,
		SessionID:     output.SessionID,
		AccessToken:   output.AccessToken,
		AccessExpiry:  output.AccessExpiry,
		RefreshToken:  output.RefreshToken,
		RefreshExpiry: output.RefreshExpiry,
		CreatedAt:     output.CreatedAt,
	}

	// Write response
	httpresponse.Created(w, response)
}
