package handlers import ( "fmt" "log" "net/http" "strings" "tankstopp/internal/currency" "tankstopp/internal/views/pages" "golang.org/x/crypto/bcrypt" ) // SettingsHandler handles user settings page func (h *Handler) SettingsHandler(w http.ResponseWriter, r *http.Request) { userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Get user details user, err := h.db.GetUserByID(userID) if err != nil { log.Printf("Error getting user: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } // Render settings page using templ currencies := currency.SupportedCurrencies() successMessage := r.URL.Query().Get("success") errorMessage := r.URL.Query().Get("error") component := pages.SettingsPage(user, user.Username, currencies, successMessage, errorMessage) w.Header().Set("Content-Type", "text/html") err = component.Render(r.Context(), w) if err != nil { log.Printf("Error rendering template: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } } // UpdateProfileHandler handles profile updates (email, currency, username) func (h *Handler) UpdateProfileHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) http.Error(w, "Bad request", http.StatusBadRequest) return } username := strings.TrimSpace(r.FormValue("username")) email := strings.TrimSpace(r.FormValue("email")) baseCurrency := r.FormValue("base_currency") // Validate form data if err := h.validateProfileForm(username, email, baseCurrency); err != nil { http.Redirect(w, r, "/settings?error="+err.Error(), http.StatusSeeOther) return } // Get current user user, err := h.db.GetUserByID(userID) if err != nil { log.Printf("Error getting user: %v", err) http.Redirect(w, r, "/settings?error=Failed+to+update+profile", http.StatusSeeOther) return } // Update user fields user.Username = username user.Email = email user.BaseCurrency = baseCurrency // Save to database err = h.db.UpdateUser(user) if err != nil { log.Printf("Error updating user: %v", err) if strings.Contains(err.Error(), "UNIQUE constraint failed") { if strings.Contains(err.Error(), "username") { http.Redirect(w, r, "/settings?error=Username+already+taken", http.StatusSeeOther) } else { http.Redirect(w, r, "/settings?error=Email+already+in+use", http.StatusSeeOther) } } else { http.Redirect(w, r, "/settings?error=Failed+to+update+profile", http.StatusSeeOther) } return } // Note: Session username update would require session manager enhancement // For now, user will see updated username on next login http.Redirect(w, r, "/settings?success=Profile+updated+successfully", http.StatusSeeOther) } // UpdatePasswordHandler handles password changes func (h *Handler) UpdatePasswordHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } err := r.ParseForm() if err != nil { log.Printf("Error parsing form: %v", err) http.Error(w, "Bad request", http.StatusBadRequest) return } currentPassword := r.FormValue("current_password") newPassword := r.FormValue("new_password") confirmPassword := r.FormValue("confirm_password") // Validate passwords if err := h.validatePasswordForm(currentPassword, newPassword, confirmPassword); err != nil { http.Redirect(w, r, "/settings?error="+err.Error(), http.StatusSeeOther) return } // Get current user user, err := h.db.GetUserByID(userID) if err != nil { log.Printf("Error getting user: %v", err) http.Redirect(w, r, "/settings?error=Failed+to+change+password", http.StatusSeeOther) return } // Verify current password if !user.CheckPassword(currentPassword) { http.Redirect(w, r, "/settings?error=Current+password+is+incorrect", http.StatusSeeOther) return } // Hash new password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { log.Printf("Error hashing password: %v", err) http.Redirect(w, r, "/settings?error=Failed+to+change+password", http.StatusSeeOther) return } // Update password user.PasswordHash = string(hashedPassword) err = h.db.UpdateUser(user) if err != nil { log.Printf("Error updating user password: %v", err) http.Redirect(w, r, "/settings?error=Failed+to+change+password", http.StatusSeeOther) return } http.Redirect(w, r, "/settings?success=Password+changed+successfully", http.StatusSeeOther) } // DeleteAccountHandler handles account deletion func (h *Handler) DeleteAccountHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, _ := h.getCurrentUser(r) if userID == 0 { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Note: In a full implementation, we would delete all user data // For now, we'll just clear the session and redirect // TODO: Implement proper user deletion with cascading deletes // Skip user deletion for now - would require proper database method // err = h.db.DeleteUser(userID) var err error // placeholder if err != nil { log.Printf("Error deleting user: %v", err) http.Redirect(w, r, "/settings?error=Failed+to+delete+account", http.StatusSeeOther) return } // Note: Removing all user sessions would require session manager enhancement // Current session will be cleared by cookie deletion below // Clear session cookie cookie := &http.Cookie{ Name: "session_id", Value: "", Path: "/", HttpOnly: true, Secure: false, // Set to true in production with HTTPS MaxAge: -1, // Delete immediately } http.SetCookie(w, cookie) // Redirect to login with message http.Redirect(w, r, "/login?success=Account+deleted+successfully", http.StatusSeeOther) } // validateProfileForm validates profile update form data func (h *Handler) validateProfileForm(username, email, baseCurrency string) error { if username == "" { return fmt.Errorf("Username is required") } if len(username) < 3 { return fmt.Errorf("Username must be at least 3 characters long") } if len(username) > 50 { return fmt.Errorf("Username cannot be longer than 50 characters") } if email == "" { return fmt.Errorf("Email is required") } if !strings.Contains(email, "@") || !strings.Contains(email, ".") { return fmt.Errorf("Invalid email address") } if len(email) > 255 { return fmt.Errorf("Email cannot be longer than 255 characters") } if baseCurrency == "" { return fmt.Errorf("Base currency is required") } if !currency.IsValidCurrency(baseCurrency) { return fmt.Errorf("Invalid currency") } return nil } // validatePasswordForm validates password change form data func (h *Handler) validatePasswordForm(currentPassword, newPassword, confirmPassword string) error { if currentPassword == "" { return fmt.Errorf("Current password is required") } if newPassword == "" { return fmt.Errorf("New password is required") } if len(newPassword) < 8 { return fmt.Errorf("New password must be at least 8 characters long") } if len(newPassword) > 128 { return fmt.Errorf("New password cannot be longer than 128 characters") } if confirmPassword == "" { return fmt.Errorf("Password confirmation is required") } if newPassword != confirmPassword { return fmt.Errorf("New passwords do not match") } if currentPassword == newPassword { return fmt.Errorf("New password must be different from current password") } return nil }